branch: elpa/projectile
commit d38233dcfe431b45bb5bf749bd5540d4674d1674
Author: Bozhidar Batsov <[email protected]>
Commit: Bozhidar Batsov <[email protected]>
Lean up the tests with a shared matcher and helper consolidation
- Add a :to-have-same-items-as custom matcher (order-independent list
equality with a missing/extra failure message) and use it in place of
the (sort X #'string<) :to-equal (sort ...) dance across the watch,
indexing, async, consult and file-kinds specs.
- Add a shared projectile-test-match-sig helper and drop the two nearly
identical per-file --sig copies (scan-async, search-review).
- Make the search-review --wait delegate to the shared
projectile-test-wait-for pump instead of re-rolling the event loop.
No behavior change; 889 specs green.
---
test/projectile-async-test.el | 8 +++-----
test/projectile-consult-test.el | 4 ++--
test/projectile-file-kinds-test.el | 6 +++---
test/projectile-indexing-test.el | 2 +-
test/projectile-scan-async-test.el | 27 +++++++++------------------
test/projectile-search-review-test.el | 26 +++++---------------------
test/projectile-test-helpers.el | 30 ++++++++++++++++++++++++++++++
test/projectile-watch-test.el | 16 +++++++---------
8 files changed, 60 insertions(+), 59 deletions(-)
diff --git a/test/projectile-async-test.el b/test/projectile-async-test.el
index d91a05686e..dad5ed02e6 100644
--- a/test/projectile-async-test.el
+++ b/test/projectile-async-test.el
@@ -309,11 +309,9 @@ that stores into it as the async callback."
(projectile-git-use-fd nil))
(call-process "git" nil nil nil "init")
(call-process "git" nil nil nil "add" "-A")
- (let ((sync (sort (projectile-dir-files-alien default-directory)
#'string<))
- (async (sort (copy-sequence
- (projectile--dir-files-alien-await
default-directory))
- #'string<)))
- (expect async :to-equal sync)
+ (let ((sync (projectile-dir-files-alien default-directory))
+ (async (projectile--dir-files-alien-await default-directory)))
+ (expect async :to-have-same-items-as sync)
(expect async :to-contain "a.el")
(expect async :to-contain "src/b.el"))))))
diff --git a/test/projectile-consult-test.el b/test/projectile-consult-test.el
index ea09632d82..4da9a1225a 100644
--- a/test/projectile-consult-test.el
+++ b/test/projectile-consult-test.el
@@ -77,9 +77,9 @@
(output (with-temp-buffer
(apply #'process-file (car wrapped) nil t nil (cdr
wrapped))
(buffer-string)))
- (files (sort (split-string output "\n" t) #'string<)))
+ (files (split-string output "\n" t)))
;; Same set Projectile itself would index, one per line (no NULs).
- (expect files :to-equal '("a.el" "src/b.el"))
+ (expect files :to-have-same-items-as '("a.el" "src/b.el"))
(expect output :not :to-match "\0"))))))
(it "errors when external-command indexing is disabled"
diff --git a/test/projectile-file-kinds-test.el
b/test/projectile-file-kinds-test.el
index 9212038b4d..06d93a42bf 100644
--- a/test/projectile-file-kinds-test.el
+++ b/test/projectile-file-kinds-test.el
@@ -253,9 +253,9 @@
(setq captured choices)
nil))
(projectile--find-file-of-kind (assq :controller
projectile--rails-file-kinds))
- (expect (sort (copy-sequence captured) #'string<)
- :to-equal '("app/controllers/products_controller.rb"
- "app/controllers/users_controller.rb")))))))
+ (expect captured
+ :to-have-same-items-as
'("app/controllers/products_controller.rb"
+
"app/controllers/users_controller.rb")))))))
(describe "projectile-toggle-related-file"
(it "opens the single counterpart immediately"
diff --git a/test/projectile-indexing-test.el b/test/projectile-indexing-test.el
index d57359d701..5bf1e55348 100644
--- a/test/projectile-indexing-test.el
+++ b/test/projectile-indexing-test.el
@@ -987,7 +987,7 @@
(projectile-dir-files root)))
(hybrid (let ((projectile-indexing-method 'hybrid))
(projectile-dir-files root))))
- (expect (sort native #'string<) :to-equal (sort hybrid #'string<))
+ (expect native :to-have-same-items-as hybrid)
(expect native :to-contain "src/main.c")
(expect native :to-contain "README.md")
(expect native :to-contain "keep.text")
diff --git a/test/projectile-scan-async-test.el
b/test/projectile-scan-async-test.el
index c299c83362..07596886c9 100644
--- a/test/projectile-scan-async-test.el
+++ b/test/projectile-scan-async-test.el
@@ -74,15 +74,6 @@ project root. BODY runs with `default-directory' at the
root and
projectile-replace--case-fold t))
buf))
-(defun projectile-scan-async-test--sig (matches root)
- "Return a comparable signature (relpath line col string) list for MATCHES."
- (mapcar (lambda (m)
- (list (file-relative-name (projectile-replace--match-file m) root)
- (projectile-replace--match-line m)
- (projectile-replace--match-column m)
- (projectile-replace--match-string m)))
- matches))
-
(defun projectile-scan-async-test--candidates (root)
"Return the regexp-search candidate files under ROOT for term foo."
(projectile-replace--candidates "foo" nil t root))
@@ -121,10 +112,10 @@ project root. BODY runs with `default-directory' at the
root and
(with-current-buffer buf
(expect projectile-replace--scanning :to-be nil)
(expect projectile-replace--scan-timer :to-be nil)
- (expect (projectile-scan-async-test--sig
+ (expect (projectile-test-match-sig
projectile-replace--matches root)
:to-equal
- (projectile-scan-async-test--sig sync root))))
+ (projectile-test-match-sig sync root))))
(kill-buffer buf)))))
(it "streams matches in chunk by chunk and ends with the full set"
@@ -151,10 +142,10 @@ project root. BODY runs with `default-directory' at the
root and
(with-current-buffer buf
(expect projectile-replace--scanning :to-be nil)
(expect projectile-replace--scan-timer :to-be nil)
- (expect (projectile-scan-async-test--sig
+ (expect (projectile-test-match-sig
projectile-replace--matches root)
:to-equal
- (projectile-scan-async-test--sig sync root))))
+ (projectile-test-match-sig sync root))))
(kill-buffer buf)))))
(it "keeps case sensitivity in a late chunk (parity with sync)"
@@ -179,11 +170,11 @@ project root. BODY runs with `default-directory' at the
root and
(projectile-replace--gather-async candidates "foo" buf nil)
(projectile-scan-async-test--pump buf)
(with-current-buffer buf
- (expect (projectile-scan-async-test--sig
+ (expect (projectile-test-match-sig
projectile-replace--matches root)
:to-equal
- (projectile-scan-async-test--sig sync root))
- (expect (projectile-scan-async-test--sig
+ (projectile-test-match-sig sync root))
+ (expect (projectile-test-match-sig
projectile-replace--matches root)
:not :to-contain '("e.txt" 1 0 "FOO"))))
(kill-buffer buf)))))
@@ -204,10 +195,10 @@ project root. BODY runs with `default-directory' at the
root and
(with-current-buffer buf
(expect (length projectile-replace--matches) :to-equal 3)
(expect projectile-replace--truncated :to-be-truthy)
- (expect (projectile-scan-async-test--sig
+ (expect (projectile-test-match-sig
projectile-replace--matches root)
:to-equal
- (projectile-scan-async-test--sig sync root))))
+ (projectile-test-match-sig sync root))))
(kill-buffer buf)))))
(it "runs ON-DONE in the buffer once scanning finishes"
diff --git a/test/projectile-search-review-test.el
b/test/projectile-search-review-test.el
index 5ae732f8a8..edd733f33d 100644
--- a/test/projectile-search-review-test.el
+++ b/test/projectile-search-review-test.el
@@ -104,16 +104,6 @@ REGEXP-P selects `projectile-search-regexp-review'."
files :test #'equal))
(sort files #'string<))))
-(defun projectile-search-review-test--sig (matches root)
- "Return a comparable (relpath line col string) signature list for MATCHES."
- (sort (mapcar (lambda (m)
- (list (file-relative-name (projectile-replace--match-file m)
root)
- (projectile-replace--match-line m)
- (projectile-replace--match-column m)
- (projectile-replace--match-string m)))
- matches)
- (lambda (a b) (string< (format "%S" a) (format "%S" b)))))
-
(describe "projectile-search-review (literal)"
(it "finds every literal match grouped by file, read-only, no apply keys"
(projectile-search-review-test--with-project
@@ -332,15 +322,9 @@ REGEXP-P selects `projectile-search-regexp-review'."
;;; Ripgrep fast-path
(defun projectile-search-review-test--wait (buf)
- "Pump events until BUF's ripgrep scan finishes (or a timeout elapses).
-Pumps the general event loop (not just the scan process): once the rg
-process has exited, `accept-process-output' on that dead process can
-return immediately without dispatching its queued sentinel - the sentinel
-is what runs `projectile-search--rg-finish' and clears the scanning flag."
- (with-current-buffer buf
- (let ((limit (+ (float-time) 10)))
- (while (and projectile-replace--scanning (< (float-time) limit))
- (accept-process-output nil 0.05)))))
+ "Pump events until BUF's ripgrep scan finishes (or a timeout elapses)."
+ (projectile-test-wait-for
+ (lambda () (not (buffer-local-value 'projectile-replace--scanning buf)))))
(describe "projectile-search--rg byte->char column conversion"
(it "counts a multibyte prefix as characters, not bytes"
@@ -666,9 +650,9 @@ is what runs `projectile-search--rg-finish' and clears the
scanning flag."
root term term nil t t)
(projectile-search--gather-rg buf term nil)
(projectile-search-review-test--wait buf)
- (expect (projectile-search-review-test--sig
+ (expect (projectile-test-match-sig
(buffer-local-value 'projectile-replace--matches buf) root)
:to-equal
- (projectile-search-review-test--sig elisp root))))))
+ (projectile-test-match-sig elisp root))))))
;;; projectile-search-review-test.el ends here
diff --git a/test/projectile-test-helpers.el b/test/projectile-test-helpers.el
index 3afd8a8cfe..ab0bfdc05a 100644
--- a/test/projectile-test-helpers.el
+++ b/test/projectile-test-helpers.el
@@ -190,6 +190,36 @@ process filters and sentinels get a chance to run.
TIMEOUT defaults to
(accept-process-output nil 0.05))
result))
+
+;;; Custom matchers
+
+(buttercup-define-matcher :to-have-same-items-as (actual expected)
+ "Match when ACTUAL and EXPECTED hold the same items, ignoring order.
+A readable stand-in for `(sort ...) :to-equal (sort ...)' - the failure
+message reports exactly which items are missing and which are extra."
+ (let* ((actual (funcall actual))
+ (expected (funcall expected))
+ (missing (cl-set-difference expected actual :test #'equal))
+ (extra (cl-set-difference actual expected :test #'equal)))
+ (cons (and (null missing) (null extra))
+ (format "Expected %S to have the same items as %S (missing: %S,
extra: %S)"
+ actual expected missing extra))))
+
+;;; Match helpers (reviewable search/replace)
+
+(defun projectile-test-match-sig (matches root)
+ "Return a stable, comparable signature for MATCHES relative to ROOT.
+Each `projectile-replace--match' becomes (RELPATH LINE COLUMN STRING);
+the list is sorted so two gatherings of the same matches compare `equal'
+regardless of the order they were collected in."
+ (sort (mapcar (lambda (m)
+ (list (file-relative-name (projectile-replace--match-file m)
root)
+ (projectile-replace--match-line m)
+ (projectile-replace--match-column m)
+ (projectile-replace--match-string m)))
+ matches)
+ (lambda (a b) (string< (format "%S" a) (format "%S" b)))))
+
(defun file-handler-for-tests (operation &rest args)
"Handler for # files.
Just delegates OPERATION and ARGS for all operations except for
diff --git a/test/projectile-watch-test.el b/test/projectile-watch-test.el
index d528ca19bd..bcfaf042dc 100644
--- a/test/projectile-watch-test.el
+++ b/test/projectile-watch-test.el
@@ -66,10 +66,9 @@ watch bookkeeping into each other, caching is on (transient)
and
(describe "projectile--watch-directories"
(it "derives the project root and the directory chain of every cached file"
(let ((root "/tmp/watched-project/"))
- (expect (sort (projectile--watch-directories
- root '("a.el" "src/b.el" "src/sub/c.el" "doc/d.adoc"))
- #'string<)
- :to-equal
+ (expect (projectile--watch-directories
+ root '("a.el" "src/b.el" "src/sub/c.el" "doc/d.adoc"))
+ :to-have-same-items-as
(list root
(concat root "doc/")
(concat root "src/")
@@ -83,9 +82,8 @@ watch bookkeeping into each other, caching is on (transient)
and
(let ((root (projectile-project-root)))
(projectile-cache-project root '("a.el" "src/b.el" "src/sub/c.el"))
(expect (spy-calls-count 'file-notify-add-watch) :to-equal 3)
- (expect (sort (mapcar #'cdr (gethash root
projectile--project-watches))
- #'string<)
- :to-equal
+ (expect (mapcar #'cdr (gethash root projectile--project-watches))
+ :to-have-same-items-as
(list root (concat root "src/") (concat root
"src/sub/")))))))
(it "skips directories that no longer exist on disk"
@@ -323,8 +321,8 @@ watch bookkeeping into each other, caching is on
(transient) and
(projectile--handle-watch-event root (list descriptor 'created
file))
(projectile--handle-watch-event root (list descriptor 'created
file)))
(projectile--process-watch-events root)
- (expect (sort (gethash root projectile-projects-cache) #'string<)
- :to-equal '("a.el" "b.el"))
+ (expect (gethash root projectile-projects-cache)
+ :to-have-same-items-as '("a.el" "b.el"))
(projectile--unwatch-project root)))))
(it "invalidates the cache when the project root itself is deleted"