aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--modules/modeline-config.el8
-rw-r--r--tests/test-modeline-config-vc-cache-key.el56
2 files changed, 62 insertions, 2 deletions
diff --git a/modules/modeline-config.el b/modules/modeline-config.el
index 61ab0f9d..5afe226e 100644
--- a/modules/modeline-config.el
+++ b/modules/modeline-config.el
@@ -129,8 +129,12 @@ Uses built-in cached values for performance.")
cj/modeline-vc-cache-set-p nil))
(defun cj/modeline-vc-cache-key (file)
- "Return the cache key for FILE."
- (list file cj/modeline-vc-show-remote))
+ "Return the cache key for FILE.
+Includes the resolved `file-truename' so that if FILE is a symlink whose
+target moves to a different VC tree, the key changes and the cache is not
+served a stale backend. The extra `file-truename' is one stat per refresh,
+cheap next to the VC calls the cache avoids."
+ (list file (file-truename file) cj/modeline-vc-show-remote))
(defun cj/modeline-vc-cache-valid-p (key now)
"Return non-nil when cached VC data is valid for KEY at NOW."
diff --git a/tests/test-modeline-config-vc-cache-key.el b/tests/test-modeline-config-vc-cache-key.el
new file mode 100644
index 00000000..ae869f4b
--- /dev/null
+++ b/tests/test-modeline-config-vc-cache-key.el
@@ -0,0 +1,56 @@
+;;; test-modeline-config-vc-cache-key.el --- Tests for VC modeline cache key -*- lexical-binding: t; -*-
+
+;;; Commentary:
+;; The VC modeline cache keys on the file. A symlink whose target moves to a
+;; different VC tree must invalidate the cache, so the key includes the
+;; resolved `file-truename', not just the symlink path.
+
+;;; Code:
+
+(require 'ert)
+(require 'cl-lib)
+
+(add-to-list 'load-path (expand-file-name "modules" user-emacs-directory))
+(require 'modeline-config)
+
+;;; Normal Cases
+
+(ert-deftest test-modeline-vc-cache-key-includes-truename ()
+ "Normal: the cache key includes the resolved truename of the file."
+ (let ((f (make-temp-file "cj-mlkey-")))
+ (unwind-protect
+ (should (member (file-truename f) (cj/modeline-vc-cache-key f)))
+ (delete-file f))))
+
+;;; Boundary Cases
+
+(ert-deftest test-modeline-vc-cache-key-changes-when-symlink-target-moves ()
+ "Boundary: re-pointing a symlink to a new target changes the cache key.
+The symlink path is identical both times; only its truename differs, so a
+key that ignored the truename would serve a stale VC backend."
+ (let* ((dir (make-temp-file "cj-mlkey-dir-" t))
+ (target-a (expand-file-name "a" dir))
+ (target-b (expand-file-name "b" dir))
+ (link (expand-file-name "link" dir)))
+ (unwind-protect
+ (progn
+ (write-region "" nil target-a)
+ (write-region "" nil target-b)
+ (make-symbolic-link target-a link)
+ (let ((key-a (cj/modeline-vc-cache-key link)))
+ (delete-file link)
+ (make-symbolic-link target-b link)
+ (let ((key-b (cj/modeline-vc-cache-key link)))
+ (should-not (equal key-a key-b)))))
+ (delete-directory dir t))))
+
+(ert-deftest test-modeline-vc-cache-key-stable-for-same-file ()
+ "Boundary: the key is stable across calls for an unchanged file."
+ (let ((f (make-temp-file "cj-mlkey-stable-")))
+ (unwind-protect
+ (should (equal (cj/modeline-vc-cache-key f)
+ (cj/modeline-vc-cache-key f)))
+ (delete-file f))))
+
+(provide 'test-modeline-config-vc-cache-key)
+;;; test-modeline-config-vc-cache-key.el ends here