aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-05-10 12:19:17 -0500
committerCraig Jennings <c@cjennings.net>2026-05-10 12:19:17 -0500
commita7cc492131361195ad08d433c64c7c76b255bc8c (patch)
treefa6cc368494c248ac97d783d2b0171a4ebf42ce8
parent3003683a24cf38dbd2eeeaee6244ad0c1bbe72ee (diff)
downloaddotemacs-a7cc492131361195ad08d433c64c7c76b255bc8c.tar.gz
dotemacs-a7cc492131361195ad08d433c64c7c76b255bc8c.zip
test(vterm): cover the copy-mode exit chain end-to-end
The unit tests for the cursor-restoration hook only exercised the helper in isolation. The real integration -- toggling the minor mode and watching the hook fire as part of the chain -- wasn't covered. If `vterm-copy-mode-done' or `cj/vterm-copy-mode-cancel' broke their exit semantics (or our hook stopped firing on `vterm-copy-mode-hook'), the unit tests would still pass but the cursor would stay stuck on the bar in real use. Add five integration tests that toggle the actual minor mode through stubbed `vterm--enter-copy-mode' / `vterm--exit-copy-mode' (so we don't need a live vterm process) and assert the cursor moves through the full lifecycle: nil -> bar on enter, bar -> killed-local on exit. Cover all three exit paths Craig hits in normal use: - vterm-copy-mode -1 directly (the toggle) - vterm-copy-mode-done with an active region (M-w / RET) - vterm-copy-mode-done with no region (line-selection branch) - cj/vterm-copy-mode-cancel (C-g / <escape>) Plus a multi-cycle test so a regression in `kill-local-variable' handling shows up.
-rw-r--r--tests/test-vterm-copy-mode-cursor.el93
1 files changed, 93 insertions, 0 deletions
diff --git a/tests/test-vterm-copy-mode-cursor.el b/tests/test-vterm-copy-mode-cursor.el
index ee72a0bb..85fc2897 100644
--- a/tests/test-vterm-copy-mode-cursor.el
+++ b/tests/test-vterm-copy-mode-cursor.el
@@ -12,10 +12,26 @@
;;; Code:
(require 'ert)
+(require 'cl-lib)
+(require 'package)
+(setq package-user-dir (expand-file-name "elpa" user-emacs-directory))
+(package-initialize)
(add-to-list 'load-path (expand-file-name "modules" user-emacs-directory))
(defvar vterm-copy-mode nil)
(require 'vterm-config)
+(require 'vterm)
+
+(defmacro test-vterm-copy-mode-cursor--in-fake-vterm-buffer (&rest body)
+ "Run BODY in a temp buffer pretending to be a live vterm.
+Stubs `vterm--enter-copy-mode' and `vterm--exit-copy-mode' so toggling
+`vterm-copy-mode' doesn't try to talk to a real vterm process."
+ (declare (indent 0))
+ `(cl-letf (((symbol-function 'vterm--enter-copy-mode) #'ignore)
+ ((symbol-function 'vterm--exit-copy-mode) #'ignore))
+ (with-temp-buffer
+ (setq-local major-mode 'vterm-mode)
+ ,@body)))
(ert-deftest test-vterm-copy-mode-cursor-restored-on-enter ()
"Normal: entering copy-mode with cursor-type nil sets a visible cursor."
@@ -47,5 +63,82 @@
(should (memq #'cj/--vterm-copy-mode-restore-cursor
vterm-copy-mode-hook)))
+(ert-deftest test-vterm-copy-mode-cursor-end-to-end-via-mode-toggle ()
+ "Normal: toggling `vterm-copy-mode' on then off via the real minor mode
+command produces the visible cursor on entry and removes the override on
+exit. This exercises the full path -- mode body, hook registration, our
+restore function -- not just the helper in isolation."
+ (test-vterm-copy-mode-cursor--in-fake-vterm-buffer
+ (setq-local cursor-type nil)
+ ;; Enter copy-mode through the actual minor-mode command, not by
+ ;; let-binding the variable. This fires `vterm-copy-mode-hook'.
+ (vterm-copy-mode 1)
+ (should (eq vterm-copy-mode t))
+ (should (equal cursor-type '(bar . 3)))
+ ;; Exit through the same path.
+ (vterm-copy-mode -1)
+ (should (eq vterm-copy-mode nil))
+ (should-not (local-variable-p 'cursor-type))))
+
+(ert-deftest test-vterm-copy-mode-cursor-end-to-end-via-copy-done ()
+ "Normal: `vterm-copy-mode-done' (M-w / RET binding) toggles copy-mode
+off and triggers cursor restoration. This is the path the user takes
+most often -- copy and exit in one keystroke."
+ (test-vterm-copy-mode-cursor--in-fake-vterm-buffer
+ (setq-local cursor-type nil)
+ (vterm-copy-mode 1)
+ (should (eq vterm-copy-mode t))
+ (should (equal cursor-type '(bar . 3)))
+ (insert "selectable text on this line")
+ (set-mark (point-min))
+ (goto-char (point-max))
+ (vterm-copy-mode-done nil)
+ (should (eq vterm-copy-mode nil))
+ (should-not (local-variable-p 'cursor-type))))
+
+(ert-deftest test-vterm-copy-mode-cursor-end-to-end-via-cancel ()
+ "Normal: `cj/vterm-copy-mode-cancel' (C-g / <escape> binding) toggles
+copy-mode off and triggers cursor restoration even when no region was
+selected -- the cancel path skips the kill-ring step entirely."
+ (test-vterm-copy-mode-cursor--in-fake-vterm-buffer
+ (setq-local cursor-type nil)
+ (vterm-copy-mode 1)
+ (should (equal cursor-type '(bar . 3)))
+ (cj/vterm-copy-mode-cancel)
+ (should (eq vterm-copy-mode nil))
+ (should-not (local-variable-p 'cursor-type))))
+
+(ert-deftest test-vterm-copy-mode-cursor-end-to-end-via-copy-done-no-region ()
+ "Boundary: `vterm-copy-mode-done' called with no active region falls
+into its line-selection branch. The branch calls vterm-internal
+helpers that aren't safe in a fake buffer, so stub them to point-min /
+point-max. The exit-and-fire-hook chain at the function's tail must
+still run; cursor restoration must still happen."
+ (cl-letf (((symbol-function 'vterm--get-beginning-of-line)
+ (lambda (&rest _) (point-min)))
+ ((symbol-function 'vterm--get-end-of-line)
+ (lambda (&rest _) (point-max))))
+ (test-vterm-copy-mode-cursor--in-fake-vterm-buffer
+ (insert "line content")
+ (setq-local cursor-type nil)
+ (vterm-copy-mode 1)
+ (should (equal cursor-type '(bar . 3)))
+ (deactivate-mark)
+ (should-not (use-region-p))
+ (vterm-copy-mode-done nil)
+ (should (eq vterm-copy-mode nil))
+ (should-not (local-variable-p 'cursor-type)))))
+
+(ert-deftest test-vterm-copy-mode-cursor-survives-multiple-cycles ()
+ "Boundary: enter/exit/enter/exit cycles don't accumulate buffer-local
+state. The cursor goes back and forth cleanly."
+ (test-vterm-copy-mode-cursor--in-fake-vterm-buffer
+ (setq-local cursor-type nil)
+ (dotimes (_ 3)
+ (vterm-copy-mode 1)
+ (should (equal cursor-type '(bar . 3)))
+ (vterm-copy-mode -1)
+ (should-not (local-variable-p 'cursor-type)))))
+
(provide 'test-vterm-copy-mode-cursor)
;;; test-vterm-copy-mode-cursor.el ends here