branch: elpa/projectile
commit b33b988356b4007e22fedc89959f1394acab39c4
Author: Bozhidar Batsov <[email protected]>
Commit: Bozhidar Batsov <[email protected]>

    Test the ripgrep fast-path against the elisp scan in CI
    
    Install ripgrep in the CI test job so the search reviewer's rg fast-path
    is actually exercised (the specs self-skip when rg is absent).  Add a
    parity spec asserting the rg fast-path and the portable elisp scan return
    the same match set over a shared fixture - the core correctness claim of
    the fast-path, previously only tested per-branch.
    
    Enabling rg in CI also surfaced a flaky wait helper: the pre-existing rg
    specs pumped `accept-process-output' on the scan *process*, which returns
    immediately once that process has exited without dispatching its queued
    sentinel - so `projectile-search--rg-finish' never ran and the scan flag
    never cleared (hung on Emacs 28.2).  Pump the general event loop instead.
---
 .github/workflows/test.yml            |  8 +++++++
 test/projectile-search-review-test.el | 42 +++++++++++++++++++++++++++++++++--
 2 files changed, 48 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 1e98f2aeef..43649458e9 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -27,6 +27,14 @@ jobs:
     - name: Check out the source code
       uses: actions/checkout@v6
 
+    # ripgrep powers the search reviewer's fast-path; install it so the specs
+    # that exercise the real `rg' subprocess actually run in CI instead of
+    # being skipped (they self-skip when `rg' is absent).
+    - name: Install ripgrep
+      run: |
+        apt-get update
+        apt-get install -y ripgrep
+
     - name: Test the project
       run: |
         eldev -p -dtT -C test --expect 680
diff --git a/test/projectile-search-review-test.el 
b/test/projectile-search-review-test.el
index 0d9a1e4965..5ae732f8a8 100644
--- a/test/projectile-search-review-test.el
+++ b/test/projectile-search-review-test.el
@@ -104,6 +104,16 @@ 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
@@ -322,11 +332,15 @@ 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)."
+  "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 projectile-replace--scan-process 0.05)))))
+        (accept-process-output nil 0.05)))))
 
 (describe "projectile-search--rg byte->char column conversion"
   (it "counts a multibyte prefix as characters, not bytes"
@@ -633,4 +647,28 @@ REGEXP-P selects `projectile-search-regexp-review'."
                            projectile-replace--matches)
                   :to-be nil))))))
 
+(describe "projectile-search-review ripgrep vs elisp parity"
+  (it "the ripgrep fast-path finds the same matches as the elisp scan"
+    (assume (executable-find "rg") "needs a real ripgrep")
+    (projectile-search-review-test--with-project
+        (("a.txt" . "foo one\nno match here\nfoo two\n")
+         ("sub/b.txt" . "prefix foo suffix\n")
+         ("c.txt" . "nothing to see\n"))
+      (let* ((root default-directory)
+             (term "foo")
+             ;; elisp reference scan (the portable, deterministic path)
+             (candidates (projectile-replace--candidates term t t root))
+             (elisp (plist-get (projectile-replace--gather candidates term)
+                               :matches))
+             ;; ripgrep fast-path
+             (buf (get-buffer-create projectile-search-buffer-name)))
+        (projectile-replace--seed buf #'projectile-search-mode
+                                  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
+                 (buffer-local-value 'projectile-replace--matches buf) root)
+                :to-equal
+                (projectile-search-review-test--sig elisp root))))))
+
 ;;; projectile-search-review-test.el ends here

Reply via email to