aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorBozhidar Batsov <bozhidar@toptal.com>2026-04-26 07:14:41 +0100
committerBozhidar Batsov <bozhidar@toptal.com>2026-04-26 07:14:41 +0100
commit69adda8f55aaeedcee77948318c3e389665019db (patch)
tree90b33a5edeea12c5ca76f381e40234a2293493d8 /test
parent67981d6d93b957edf3359d94f6216837427834bc (diff)
Memoize per-function nil results in the root cache (#1836)
Previously a root function returning nil stored nil in the cache, which is indistinguishable from a missing entry, so on the next call the function was re-run. When a project's root is found by the third function in projectile-project-root-functions, the first two re-walked the tree on every call - and projectile-project-root is hot in mode-line / company / spaceline updates. Store the symbol 'none for unsuccessful entries and treat it as a cache hit on lookup. The overall-failure marker already used 'none in a separate slot, so this just extends the same convention to the per-function entries.
Diffstat (limited to 'test')
-rw-r--r--test/projectile-test.el26
1 files changed, 25 insertions, 1 deletions
diff --git a/test/projectile-test.el b/test/projectile-test.el
index 52f24b1..88d1da7 100644
--- a/test/projectile-test.el
+++ b/test/projectile-test.el
@@ -1283,7 +1283,31 @@ Just delegates OPERATION and ARGS for all operations except for`shell-command`'.
;; Second "buffer": same default-directory, different override.
;; The cache must not return the previous buffer's answer.
(let ((projectile-project-root beta-root))
- (expect (projectile-project-root) :to-equal beta-root)))))))
+ (expect (projectile-project-root) :to-equal beta-root))))))
+
+ (it "caches per-function nil results so earlier misses aren't re-walked (#1836)"
+ (projectile-test-with-sandbox
+ (projectile-test-with-files
+ ("projectA/.git/"
+ "projectA/src/")
+ (let* ((calls 0)
+ (always-nil (lambda (_dir) (cl-incf calls) nil))
+ (projectile-project-root-functions
+ (list always-nil 'projectile-root-bottom-up))
+ (projectile-project-root-files-bottom-up '(".git"))
+ (dir (expand-file-name "projectA/src/"))
+ (projectile-project-root-cache (make-hash-table :test 'equal)))
+ ;; first call: always-nil is invoked, then bottom-up wins
+ (expect (file-truename (projectile-project-root dir))
+ :to-equal (file-truename (expand-file-name "projectA/")))
+ (expect calls :to-equal 1)
+ ;; second call: the cached 'none for always-nil should be honoured
+ (expect (file-truename (projectile-project-root dir))
+ :to-equal (file-truename (expand-file-name "projectA/")))
+ (expect calls :to-equal 1)
+ ;; and the cache holds the 'none sentinel for the failing function
+ (expect (gethash (cons always-nil dir) projectile-project-root-cache)
+ :to-be 'none))))))
(describe "projectile-file-exists-p"
(it "returns t if file exists"