aboutsummaryrefslogtreecommitdiff
path: root/tests/test-ai-vterm--collapse-split.el
diff options
context:
space:
mode:
Diffstat (limited to 'tests/test-ai-vterm--collapse-split.el')
-rw-r--r--tests/test-ai-vterm--collapse-split.el171
1 files changed, 171 insertions, 0 deletions
diff --git a/tests/test-ai-vterm--collapse-split.el b/tests/test-ai-vterm--collapse-split.el
new file mode 100644
index 00000000..ad299e47
--- /dev/null
+++ b/tests/test-ai-vterm--collapse-split.el
@@ -0,0 +1,171 @@
+;;; test-ai-vterm--collapse-split.el --- F9 collapses the agent split -*- lexical-binding: t; -*-
+
+;;; Commentary:
+;; Regression coverage for the F9 toggle-off behavior Craig reported: with
+;; several agents alive, F9 should HIDE the agent split (collapse it back to the
+;; working layout) rather than surfacing a different agent. Two cases:
+;;
+;; - Multi-window: the agent occupies a split. F9 deletes that window so the
+;; working buffer reclaims the frame -- never swaps in another agent. The
+;; prior `quit-restore-window' path went stale after the slot was reused
+;; across agents (C-F9 switching), so it surfaced a different agent.
+;; - Single-window: the agent fills the frame. F9 returns to the most-recent
+;; NON-agent buffer (the file being worked on), not another agent -- the prior
+;; `other-buffer' call could pick another live agent.
+;;
+;; Also covers the `cj/--ai-vterm-most-recent-non-agent-buffer' helper.
+
+;;; Code:
+
+(require 'ert)
+(require 'cl-lib)
+
+(add-to-list 'load-path (expand-file-name "modules" user-emacs-directory))
+(add-to-list 'load-path (expand-file-name "tests" user-emacs-directory))
+(require 'ai-vterm)
+(require 'testutil-vterm-buffers)
+
+;;; cj/--ai-vterm-most-recent-non-agent-buffer
+
+(ert-deftest test-ai-vterm--most-recent-non-agent-buffer-skips-agents ()
+ "Normal: returns a live non-agent buffer even when agents are most-recent."
+ (cj/test--kill-agent-buffers)
+ (let ((work (get-buffer-create "*test-mrna-work*"))
+ (agent-a (get-buffer-create "agent [mrna-a]"))
+ (agent-b (get-buffer-create "agent [mrna-b]")))
+ (unwind-protect
+ (save-window-excursion
+ (delete-other-windows)
+ ;; Make the agents most-recent in this window's history.
+ (set-window-buffer (selected-window) work)
+ (set-window-buffer (selected-window) agent-b)
+ (set-window-buffer (selected-window) agent-a)
+ (let ((result (cj/--ai-vterm-most-recent-non-agent-buffer)))
+ (should (bufferp result))
+ (should (buffer-live-p result))
+ (should-not (cj/--ai-vterm-buffer-p result))))
+ (when (get-buffer "*test-mrna-work*") (kill-buffer "*test-mrna-work*"))
+ (cj/test--kill-agent-buffers))))
+
+;;; Multi-window: F9 collapses the split
+
+(ert-deftest test-ai-vterm--collapse-multi-window-deletes-agent-split ()
+ "Normal/Regression: agent in a bottom split with other agents alive; F9
+collapses the split so the working buffer reclaims the frame, and no agent is
+surfaced. Before the fix, `quit-restore-window' could switch the slot to a
+different agent (stale quit-restore after slot reuse)."
+ (cj/test--kill-agent-buffers)
+ (let ((work (get-buffer-create "*test-collapse-work*"))
+ (agent-a (get-buffer-create "agent [collapse-a]"))
+ (agent-b (get-buffer-create "agent [collapse-b]"))
+ (agent-c (get-buffer-create "agent [collapse-c]"))
+ (cj/--ai-vterm-last-was-bury nil))
+ (unwind-protect
+ (save-window-excursion
+ (delete-other-windows)
+ (set-window-buffer (selected-window) work)
+ (let ((agent-win (split-window (selected-window) nil 'below)))
+ ;; Reuse the slot across agents (as C-F9 switching does) so the
+ ;; window's prev-buffer history holds another agent.
+ (set-window-buffer agent-win agent-a)
+ (set-window-buffer agent-win agent-b)
+ (set-window-buffer agent-win agent-c)
+ (select-window agent-win)
+ (should-not (one-window-p))
+ (cj/test--call-as-gui #'cj/ai-vterm)
+ (should (one-window-p))
+ (should-not (cj/--ai-vterm-displayed-agent-window))
+ (should (eq (window-buffer (selected-window)) work))))
+ (when (get-buffer "*test-collapse-work*") (kill-buffer "*test-collapse-work*"))
+ (cj/test--kill-agent-buffers))))
+
+;;; Single-window: F9 returns to a non-agent buffer
+
+(ert-deftest test-ai-vterm--collapse-single-window-returns-non-agent ()
+ "Normal/Regression: agent fills the frame, other agents alive; F9 toggles back
+to a NON-agent buffer (the working file), never another agent. Before the fix,
+`other-buffer' could pick another live agent."
+ (cj/test--kill-agent-buffers)
+ (let ((work (get-buffer-create "*test-collapse-sw-work*"))
+ (agent-a (get-buffer-create "agent [collapse-sw-a]"))
+ (agent-b (get-buffer-create "agent [collapse-sw-b]"))
+ (cj/--ai-vterm-last-was-bury nil))
+ (unwind-protect
+ (save-window-excursion
+ (delete-other-windows)
+ ;; MRU: work, then agent-b, then agent-a (current). `other-buffer'
+ ;; would pick agent-b; the fix must skip it for a non-agent.
+ (set-window-buffer (selected-window) work)
+ (set-window-buffer (selected-window) agent-b)
+ (set-window-buffer (selected-window) agent-a)
+ (should (one-window-p))
+ (let ((display-buffer-alist (cj/--ai-vterm-display-rule-list)))
+ (cj/test--call-as-gui #'cj/ai-vterm))
+ (should (one-window-p))
+ (should-not (cj/--ai-vterm-buffer-p (window-buffer (selected-window)))))
+ (when (get-buffer "*test-collapse-sw-work*") (kill-buffer "*test-collapse-sw-work*"))
+ (cj/test--kill-agent-buffers))))
+
+;;; Faithful toggle: reopen the SAME agent that was hidden
+
+(ert-deftest test-ai-vterm--dispatch-prefers-last-hidden-agent ()
+ "Regression: dispatch reopens the last-hidden agent, not the buffer-list MRU.
+After F9 hides an agent, the next F9 must reopen the SAME one even when a
+different agent is ahead of it in `buffer-list'. Falls back to the MRU when
+nothing was hidden yet or the remembered buffer was killed."
+ (cj/test--kill-agent-buffers)
+ (let ((a1 (get-buffer-create "agent [disp-mru]"))
+ (a2 (get-buffer-create "agent [disp-shown]"))
+ (cj/--ai-vterm-last-hidden-buffer nil))
+ (unwind-protect
+ (cl-letf (((symbol-function 'cj/--ai-vterm-displayed-agent-window)
+ (lambda (&optional _f) nil))
+ ((symbol-function 'cj/--ai-vterm-agent-buffers)
+ (lambda () (list a1 a2)))) ; a1 is the MRU
+ ;; No memory yet -> falls back to MRU (a1).
+ (should (equal (cj/--ai-vterm-dispatch) (cons 'redisplay-recent a1)))
+ ;; Remember a2 as last hidden -> dispatch prefers it.
+ (setq cj/--ai-vterm-last-hidden-buffer a2)
+ (should (equal (cj/--ai-vterm-dispatch) (cons 'redisplay-recent a2)))
+ ;; A killed last-hidden buffer -> falls back to MRU.
+ (let ((dead (get-buffer-create "agent [disp-dead]")))
+ (setq cj/--ai-vterm-last-hidden-buffer dead)
+ (kill-buffer dead))
+ (should (equal (cj/--ai-vterm-dispatch) (cons 'redisplay-recent a1))))
+ (cj/test--kill-agent-buffers))))
+
+(ert-deftest test-ai-vterm--toggle-roundtrip-reopens-same-agent ()
+ "Regression: hide then show brings back the agent that was on screen.
+With several agents alive and a different one most-recent in `buffer-list',
+F9 off then F9 on restores the SAME agent that was visible -- not a swap to
+another. Reproduces the \"the displayed buffer changes\" report."
+ (cj/test--kill-agent-buffers)
+ (let ((work (get-buffer-create "*test-roundtrip-work*"))
+ (a1 (get-buffer-create "agent [rt-1]"))
+ (a2 (get-buffer-create "agent [rt-2]"))
+ (cj/--ai-vterm-last-was-bury nil)
+ (cj/--ai-vterm-last-direction nil)
+ (cj/--ai-vterm-last-size nil)
+ (cj/--ai-vterm-last-hidden-buffer nil))
+ (unwind-protect
+ (save-window-excursion
+ (delete-other-windows)
+ (set-window-buffer (selected-window) work)
+ (let ((agent-win (split-window (selected-window) nil 'below)))
+ ;; a2 is the visible agent; a1 sits ahead of it in buffer-list.
+ (set-window-buffer agent-win a1)
+ (bury-buffer a1) ; a1 stays alive, demoted in MRU
+ (set-window-buffer agent-win a2)
+ (select-window agent-win)
+ (should (eq (window-buffer (cj/--ai-vterm-displayed-agent-window)) a2))
+ (let ((display-buffer-alist (cj/--ai-vterm-display-rule-list)))
+ (cj/test--call-as-gui #'cj/ai-vterm) ; off
+ (should-not (cj/--ai-vterm-displayed-agent-window))
+ (cj/test--call-as-gui #'cj/ai-vterm) ; on -> must be a2
+ (should (eq (window-buffer (cj/--ai-vterm-displayed-agent-window))
+ a2)))))
+ (when (get-buffer "*test-roundtrip-work*") (kill-buffer "*test-roundtrip-work*"))
+ (cj/test--kill-agent-buffers))))
+
+(provide 'test-ai-vterm--collapse-split)
+;;; test-ai-vterm--collapse-split.el ends here