aboutsummaryrefslogtreecommitdiff
path: root/tests/test-ui-navigation--toggle-window-split.el
blob: d93f7aada4a79e643917ca7ab3977f5e8737ea32 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
;;; test-ui-navigation--toggle-window-split.el --- Tests for toggle-window-split -*- lexical-binding: t; -*-

;;; Commentary:
;; Tests for `toggle-window-split' in ui-navigation.el.
;;
;; The function flips a 2-window split between horizontal and vertical
;; orientation.  Historically it failed when one of the two windows was
;; marked strongly-dedicated (e.g. *Org Agenda*) -- `set-window-buffer'
;; rejects buffer swaps on strongly-dedicated windows, so the
;; non-dedicated buffer never made it across and both windows ended up
;; showing the dedicated one.  The fix clears dedicated on both windows
;; before the swap.

;;; Code:

(require 'ert)

(add-to-list 'load-path (expand-file-name "modules" user-emacs-directory))
(require 'ui-navigation)

(defmacro test-toggle--with-buffers (&rest body)
  "Bind `buf-a' and `buf-b' to fresh buffers around BODY, killing both on exit.
The buffers are visible to BODY by name so it can pass them into the
window-setup macro without re-binding."
  (declare (indent 0))
  `(let ((buf-a (generate-new-buffer "*test-toggle-a*"))
         (buf-b (generate-new-buffer "*test-toggle-b*")))
     (unwind-protect
         (progn ,@body)
       (kill-buffer buf-a)
       (kill-buffer buf-b))))

(defmacro test-toggle--with-two-windows (buf-a buf-b split-fn &rest body)
  "Set up two windows displaying BUF-A and BUF-B via SPLIT-FN, run BODY.
BUF-A goes in the top/left window, BUF-B in the bottom/right.  Point
ends in the BUF-B window -- the 2nd / lower-or-right one -- because
that's the position that triggers the dedicated-window bug.  The
original repro is F8-then-toggle, which leaves selection in the agenda
(below the source buffer) when the toggle runs.  Restores the outer
window config when BODY returns.  SPLIT-FN is `split-window-below' or
`split-window-right'."
  (declare (indent 3))
  `(save-window-excursion
     (delete-other-windows)
     (set-window-buffer (selected-window) ,buf-a)
     (funcall ,split-fn)
     (other-window 1)
     (set-window-buffer (selected-window) ,buf-b)
     ,@body))

;;; Normal Cases

(ert-deftest test-ui-navigation-toggle-split-normal-no-dedicated-keeps-both-buffers ()
  "Normal: vertical split, no dedicated -- toggle preserves both buffers."
  (test-toggle--with-buffers
    (test-toggle--with-two-windows buf-a buf-b #'split-window-below
      (toggle-window-split)
      (let ((bufs (mapcar #'window-buffer (window-list))))
        (should (memq buf-a bufs))
        (should (memq buf-b bufs))))))

(ert-deftest test-ui-navigation-toggle-split-normal-dedicated-selected-window-keeps-both-buffers ()
  "Normal: selected window dedicated -- toggle still distributes both buffers.
This is the bug scenario from the *Org Agenda* report: when the
selected window is strongly-dedicated, the internal
`set-window-buffer' for the non-matching buffer is rejected and both
windows end up showing the dedicated buffer.  After the fix, dedicated
is cleared first and both buffers reach their target windows."
  (test-toggle--with-buffers
    (test-toggle--with-two-windows buf-a buf-b #'split-window-below
      (set-window-dedicated-p (selected-window) t)
      (toggle-window-split)
      (let ((bufs (mapcar #'window-buffer (window-list))))
        (should (memq buf-a bufs))
        (should (memq buf-b bufs))))))

(ert-deftest test-ui-navigation-toggle-split-normal-clears-dedicated-state ()
  "Normal: dedicated state is cleared after toggle.
The user invoked a layout change; preserving per-window dedicated
through the swap would re-introduce the same wedge on the next toggle."
  (test-toggle--with-buffers
    (test-toggle--with-two-windows buf-a buf-b #'split-window-below
      (set-window-dedicated-p (selected-window) t)
      (toggle-window-split)
      (dolist (w (window-list))
        (should-not (window-dedicated-p w))))))

;;; Boundary Cases

(ert-deftest test-ui-navigation-toggle-split-boundary-single-window-noop ()
  "Boundary: a single window in the frame -- toggle is a no-op."
  (save-window-excursion
    (delete-other-windows)
    (toggle-window-split)
    (should (= 1 (length (window-list))))))

(ert-deftest test-ui-navigation-toggle-split-boundary-three-windows-noop ()
  "Boundary: three windows in the frame -- toggle is a no-op."
  (test-toggle--with-buffers
    (save-window-excursion
      (delete-other-windows)
      (set-window-buffer (selected-window) buf-a)
      (split-window-below)
      (split-window-right)
      (let ((before-count (length (window-list))))
        (toggle-window-split)
        (should (= before-count (length (window-list))))))))

(provide 'test-ui-navigation--toggle-window-split)
;;; test-ui-navigation--toggle-window-split.el ends here