aboutsummaryrefslogtreecommitdiff
path: root/tests/e2e.sh
diff options
context:
space:
mode:
Diffstat (limited to 'tests/e2e.sh')
-rwxr-xr-xtests/e2e.sh51
1 files changed, 47 insertions, 4 deletions
diff --git a/tests/e2e.sh b/tests/e2e.sh
index 29d900b..b79c748 100755
--- a/tests/e2e.sh
+++ b/tests/e2e.sh
@@ -37,7 +37,7 @@ 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
+ tmux -L "$SOCK-restore-$$" kill-server 2>/dev/null
rm -rf "$DIR"
}
trap cleanup EXIT
@@ -226,6 +226,11 @@ t source-file "$ROOT/packaging/tmux/dingger.tmux"
segment=$(t display -p -c "$CLIENT" '#{T:status-right}' | sed 's/#\[[^]]*\]//g')
case "$segment" in *"×1"*"✓1"*) pass "segment shows ×1 ✓1 ($segment)";; *) fail "segment: [$segment]";; esac
expect "mouse binding installed" "t list-keys -T root | grep MouseDown1StatusRight | grep -c 'dingger focus'" 1
+expect "clicking the restore offer runs restore" "t list-keys -T root | grep MouseDown1StatusRight | grep -c 'restore --report'" 1
+t set -g @dingger_restore 3
+segment=$(t display -p -c "$CLIENT" '#{T:status-right}' | sed 's/#\[[^]]*\]//g')
+case "$segment" in "↺3 "*"×1"*) pass "segment offers to restore 3 sessions ($segment)";; *) fail "restore segment: [$segment]";; esac
+t set -gu @dingger_restore
# same run-shell + #{client_name} expansion as the mouse binding, via a key
t bind -n F12 run-shell "DINGGER_TMUX_SOCKET=$SOCK $DINGGER focus --client '#{client_name}'"
t send-keys -t viewer: F12
@@ -351,7 +356,7 @@ fi
# 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"
+R="$SOCK-restore-$$"
STATE2="$DIR/state2"
# tmux gives new windows the environment of the client that creates them:
# both the test and restore run with the fake pi first on PATH.
@@ -410,7 +415,10 @@ fingerprint() {
| grep -E '^(0|work)\|'
}
BEFORE=$(fingerprint)
-renv "$DINGGER" watch --interval-secs 1 --no-sounds --no-notifications >>"$DIR/daemon2.log" 2>&1 &
+# Started directly, not through a function: $! must be the daemon itself.
+env PATH="$DIR/rbin:$PATH" DINGGER_TMUX_SOCKET="$R" DINGGER_AGENT_SOCKET="$DIR/hook2.sock" \
+ DINGGER_STATE_DIR="$STATE2" DINGGER_TMUX_CONFIG=/dev/null \
+ "$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
@@ -452,7 +460,16 @@ 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 | grep -v '^ ' | cut -c1-14 | sort | tr '\n' ';'" "create work:;keep keep:;keep viewe;replace 0: 2 ;"
-renv "$DINGGER" restore >"$DIR/restore.out" 2>&1
+mbr() { renv DINGGER_BIN="$DINGGER" DINGGER_RESTORE_DIRECT=1 \
+ DINGGER_NOTIFIER="echo \"\$0|\$1\" >> $DIR/notes2" "$MENUBAR" "$@"; }
+expect "the daemon offers to restore 0 and work" \
+ "python3 -c 'import json,sys; o=json.load(open(sys.argv[1])); print([s[\"name\"] for s in o[\"sessions\"]], o[\"agents\"], o[\"interrupted\"])' $STATE2/offer.json 2>/dev/null" \
+ "['0', 'work'] 2 1" 80
+expect "and tells the status line" "r show -gv @dingger_restore" 2
+expect "the panel offers it first" "mbr --print-panel | head -1 | cut -f1,3" "↺ Restore 2 sessions · 2 agents [Not now]"
+expect "with what was interrupted" "mbr --print-panel | head -1 | cut -f2 | grep -o '1 was mid-turn'" "1 was mid-turn"
+mbr --restore
+cp "$STATE2/restore.log" "$DIR/restore.out" 2>/dev/null
expect "restore succeeds" "grep -c FAILED $DIR/restore.out" 0
expect "agents resumed with their session files" "grep -- --session $DIR/pi-args | sort" "--session $W/sessions/a b.jsonl
--session $W/sessions/gone.jsonl"
@@ -463,6 +480,8 @@ expect "the interrupted turn is reported" "grep -c '^interrupted work:agent' $D
typed_line() { r capture-pane -p -t "$1" | grep -v '^ *$' | tail -1; }
expect "a command is retyped with its quoting" "typed_line '=work:cmd' | grep -o \"sh -c 'sleep 100000; :' 'two words'\$\"" "sh -c 'sleep 100000; :' 'two words'"
expect "the zsh hook's text is retyped" "typed_line '=work:hooked' | grep -o 'slp 200000 | cat\$'" "slp 200000 | cat"
+expect "a notification summarizes" "cat $DIR/notes2" "Restored 2 tmux sessions|resumed 2 of 2 agents · 1 was mid-turn · 2 commands typed, not run"
+expect "the offer is gone" "ls $STATE2/offer.json 2>/dev/null; r show -gv @dingger_restore 2>/dev/null; mbr --print-panel | grep -c '↺'" "0"
expect "neither runs: the shell waits for Enter" \
"r display -p -t '=work:cmd' '#{pane_current_command}'; r display -p -t '=work:hooked' '#{pane_current_command}'" "bash
bash"
@@ -480,6 +499,30 @@ 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; }
+# Again, now with no tmux running at all (after a reboot, before a terminal
+# opens): restoring starts the server. Then "Not now" stays dismissed.
+names() { r list-panes -a -F '#{session_name}|#{window_index}|#{?automatic-rename,(auto),#{window_name}}|#{pane_index}|#{pane_current_path}' 2>/dev/null | grep "^$1|"; }
+WORK_BEFORE=$(names work)
+r kill-server
+expect "a new offer: all four sessions, tmux is not running" \
+ "python3 -c 'import json,sys; print(len(json.load(open(sys.argv[1]))[\"sessions\"]))' $STATE2/offer.json 2>/dev/null" 4 80
+mbr --restore work
+expect "restoring with no server starts it and rebuilds the session" "names work" "$WORK_BEFORE"
+expect "the offer shrinks to what is still missing" \
+ "python3 -c 'import json,sys; print(sorted(s[\"name\"] for s in json.load(open(sys.argv[1]))[\"sessions\"]))' $STATE2/offer.json 2>/dev/null" \
+ "['0', 'keep', 'viewer']" 80
+mbr --dismiss-offer >/dev/null
+expect "Not now: the offer is gone" "ls $STATE2/offer.json 2>/dev/null | wc -l | tr -d ' '" 0
+expect "and the status line is told" "r show -gv @dingger_restore 2>/dev/null" ""
+start_viewer
+sleep 2
+expect "it stays dismissed when a terminal starts tmux" "ls $STATE2/offer.json 2>/dev/null | wc -l | tr -d ' '" 0
+expect "the layout can still be restored by hand" "renv \"$DINGGER\" restore --dry-run | grep -c '^create keep'" 1
+
+# Every daemon this run started must stop with it.
+kill "$DAEMON_PID" "$DAEMON2_PID" 2>/dev/null
+expect "the test daemons stop" "ps -o pid= -p $DAEMON_PID,$DAEMON2_PID | wc -l | tr -d ' '" 0
+
echo
echo "$PASSES passed, $FAILS failed"
[ "$FAILS" -eq 0 ] || { echo "--- daemon log tail"; tail -30 "$DIR/daemon.log"; exit 1; }