branch: externals/debbugs
commit d014e21f9e6bd058ef5722abcd0679a83f57c4db
Author: Morgan Smith <morgan.j.sm...@outlook.com>
Commit: Michael Albinus <michael.albi...@gmx.de>

    Factor cache accesses into dedicated functions
    
    * debbugs.el (debbugs-get-cache, debbugs-put-cache): New functions.
    (debbugs-newest-bugs, debbugs-get-status): Use new functions.
    
    * test/debbugs-tests.el: Use advice to set the `cache_time' around
    the new functions.  (Bug#75789)
    (debbugs-test-newest-bug-cached): New test.
    (debbugs-test-get-status): Add test for caching behavior.
---
 debbugs.el            | 107 ++++++++++++++++++++++++++------------------------
 test/debbugs-tests.el |  57 ++++++++++++++++++++++-----
 2 files changed, 103 insertions(+), 61 deletions(-)

diff --git a/debbugs.el b/debbugs.el
index 5ab1dfc4ba..dd9b6dc09f 100644
--- a/debbugs.el
+++ b/debbugs.el
@@ -118,6 +118,33 @@ t or 0 disables caching, nil disables expiring."
                 (const :tag "Forever" nil)
                 (integer :tag "Seconds")))
 
+(defun debbugs-get-cache (bug-number)
+  "Return the cached status entry for the bug identified by BUG-NUMBER."
+  (let ((status (gethash bug-number debbugs-cache-data)))
+    (when (and status
+               (or (null debbugs-cache-expiry)
+                   (and
+                    (natnump debbugs-cache-expiry)
+                    (> (alist-get 'cache_time status)
+                       (- (float-time) debbugs-cache-expiry)))))
+      status)))
+
+(defun debbugs-put-cache (bug-number status &optional ttl)
+  "Put the STATUS entry for the bug BUG-NUMBER in the cache.
+Return STATUS."
+  (if (or (null debbugs-cache-expiry)
+          (and (natnump debbugs-cache-expiry)
+               (not (zerop debbugs-cache-expiry))))
+      (let ((cache-time (float-time)))
+        ;; Kind of a hack for TTL that assume that `debbugs-cache-expiry'
+        ;; doesn't change
+        (when (and ttl (natnump debbugs-cache-expiry))
+          (setq cache-time (+ ttl (- cache-time debbugs-cache-expiry))))
+        (puthash bug-number
+                 (cons (cons 'cache_time cache-time) status)
+                 debbugs-cache-data))
+    status))
+
 (defun debbugs-soap-invoke (operation-name &rest parameters)
   "Invoke the SOAP connection.
 OPERATION-NAME and PARAMETERS are as described in `soap-invoke'."
@@ -325,41 +352,29 @@ patch:
 (defun debbugs-newest-bugs (amount)
   "Return the list of bug numbers, according to AMOUNT (a number) latest bugs."
   (if (= amount 1)
-      ;; We cache it as bug "0" in `debbugs-cache-data'.
-      (let ((status (gethash 0 debbugs-cache-data)))
-       (unless (and
-                status
-                (or
-                 (null debbugs-cache-expiry)
-                 (and
-                  (natnump debbugs-cache-expiry)
-                  (> (alist-get 'cache_time status)
-                     (- (float-time) debbugs-cache-expiry)))))
-         ;; Due to `debbugs-gnu-completion-table', this function
-         ;; could be called in rapid sequence.  We cache temporarily
-         ;; the value nil, therefore.
-         (when (natnump debbugs-cache-expiry)
-           (puthash
-            0
-            (list (cons 'cache_time (1+ (- (float-time) debbugs-cache-expiry)))
-                  (list 'newest_bug))
-            debbugs-cache-data))
-         ;; Compute the value.
-         (setq
-          status
-          (list
-           (cons 'cache_time (float-time))
-           (cons 'newest_bug
-                 (caar
-                  (debbugs-soap-invoke
-                   debbugs-wsdl debbugs-port "newest_bugs" amount)))))
-
-         ;; Cache it.
-         (when (or (null debbugs-cache-expiry) (natnump debbugs-cache-expiry))
-           (puthash 0 status debbugs-cache-data)))
-
-       ;; Return the value, as list.
-       (list (alist-get 'newest_bug status)))
+      ;; We cache it as bug "0"
+      (let ((status (debbugs-get-cache 0)))
+        (unless status
+          ;; Due to `debbugs-gnu-completion-table', this function
+          ;; could be called in rapid sequence.  We cache temporarily
+          ;; the value nil, therefore.
+          (when (natnump debbugs-cache-expiry)
+            (debbugs-put-cache 0 (list 'newest_bug) 1))
+
+          ;; Compute the value.
+          (setq
+           status
+           (list
+            (cons 'newest_bug
+                  (caar
+                   (debbugs-soap-invoke
+                    debbugs-wsdl debbugs-port "newest_bugs" amount)))))
+
+          ;; Cache it.
+          (debbugs-put-cache 0 status))
+
+        ;; Return the value, as list.
+        (list (alist-get 'newest_bug status)))
 
     (sort
      (car (debbugs-soap-invoke
@@ -477,15 +492,8 @@ Example:
          (delq nil
           (mapcar
            (lambda (bug)
-             (let ((status (gethash bug debbugs-cache-data)))
-               (if (and
-                    status
-                    (or
-                     (null debbugs-cache-expiry)
-                     (and
-                      (natnump debbugs-cache-expiry)
-                      (> (alist-get 'cache_time status)
-                         (- (float-time) debbugs-cache-expiry)))))
+             (let ((status (debbugs-get-cache bug)))
+               (if status
                    (progn
                      (setq cached-bugs (append cached-bugs (list status)))
                      nil)
@@ -582,14 +590,9 @@ Example:
            (when (stringp (cdr y))
              (setcdr y (split-string (cdr y) ",\\| " t))))
          ;; Cache the result, and return.
-         (if (or (null debbugs-cache-expiry) (natnump debbugs-cache-expiry))
-             (puthash
-              (alist-get 'key x)
-              ;; Put also a time stamp.
-              (cons (cons 'cache_time (float-time)) (alist-get 'value x))
-              debbugs-cache-data)
-           ;; Don't cache.
-           (alist-get 'value x))))
+      (debbugs-put-cache
+       (alist-get 'key x)
+       (alist-get 'value x))))
       debbugs-soap-invoke-async-object))))
 
 (defun debbugs-get-usertag (&rest query)
diff --git a/test/debbugs-tests.el b/test/debbugs-tests.el
index 8402a4fff9..f3abea2837 100644
--- a/test/debbugs-tests.el
+++ b/test/debbugs-tests.el
@@ -89,6 +89,12 @@
           nil)
       return)))
 
+(defun debbugs-test--override-float-time (func &rest rest)
+  "Override `float-time' for FUNC with args REST."
+  (cl-letf (((symbol-function #'float-time)
+             (lambda (&optional _specified-time) 5000)))
+    (apply func rest)))
+
 (defun debbugs-test--setup ()
   "Mock network and time functions.
 These mock functions are needed to make the tests reproducible."
@@ -97,7 +103,15 @@ These mock functions are needed to make the tests 
reproducible."
 
   (add-function
    :override (symbol-function #'soap-invoke-internal)
-   #'debbugs-test--soap-invoke-internal))
+   #'debbugs-test--soap-invoke-internal)
+
+  (add-function
+   :around (symbol-function #'debbugs-get-cache)
+   #'debbugs-test--override-float-time)
+
+  (add-function
+   :around (symbol-function #'debbugs-put-cache)
+   #'debbugs-test--override-float-time))
 
 (defun debbugs-test--teardown ()
   "Restore functions to as they where before."
@@ -106,7 +120,15 @@ These mock functions are needed to make the tests 
reproducible."
 
   (remove-function
    (symbol-function #'soap-invoke-internal)
-   #'debbugs-test--soap-invoke-internal))
+   #'debbugs-test--soap-invoke-internal)
+
+  (remove-function
+   (symbol-function #'debbugs-get-cache)
+   #'debbugs-test--override-float-time)
+
+  (remove-function
+   (symbol-function #'debbugs-put-cache)
+   #'debbugs-test--override-float-time))
 
 (defmacro ert-deftest--debbugs (name args docstring &rest body)
   "The same as `ert-deftest' but runs setup and teardown functions."
@@ -138,15 +160,32 @@ These mock functions are needed to make the tests 
reproducible."
   (should (string-equal debbugs-test--soap-operation-name "newest_bugs"))
   (should (equal debbugs-test--soap-parameters '(4))))
 
+(ert-deftest--debbugs debbugs-test-newest-bug-cached ()
+  "Test getting the newest bug from the cache."
+  ;; First time we get it from the server.
+  (should (equal (debbugs-newest-bugs 1) '(0)))
+  (should (equal debbugs-test--soap-operation-name "newest_bugs"))
+  (should (equal debbugs-test--soap-parameters '(1)))
+  (setq debbugs-test--soap-operation-name nil)
+  (setq debbugs-test--soap-parameters nil)
+  ;; Now it's cached
+  (should (equal (debbugs-newest-bugs 1) '(0)))
+  (should (equal debbugs-test--soap-operation-name nil))
+  (should (equal debbugs-test--soap-parameters nil)))
+
 (ert-deftest--debbugs debbugs-test-get-status ()
   "Test \"get_status\"."
-  (cl-letf (((symbol-function #'float-time)
-             (lambda (&optional _specified-time) 5000)))
-    (should (= (float-time) 5000))
-    (should (equal (sort (car (debbugs-get-status 64064)))
-                   (sort (car debbugs-test--bug-status))))
-    (should (string-equal debbugs-test--soap-operation-name "get_status"))
-    (should (equal debbugs-test--soap-parameters '([64064])))))
+  (should (equal (sort (car (debbugs-get-status 64064)))
+                 (sort (car debbugs-test--bug-status))))
+  (should (string-equal debbugs-test--soap-operation-name "get_status"))
+  (should (equal debbugs-test--soap-parameters '([64064])))
+  (setq debbugs-test--soap-operation-name nil)
+  (setq debbugs-test--soap-parameters nil)
+  ;; cached
+  (should (equal (sort (car (debbugs-get-status 64064)))
+                 (sort (car debbugs-test--bug-status))))
+  (should (equal debbugs-test--soap-operation-name nil))
+  (should (equal debbugs-test--soap-parameters nil)))
 
 (ert-deftest--debbugs debbugs-test-get-usertag ()
   "Test \"get_usertag\"."

Reply via email to