branch: master commit 6b3923bc630d70732925b19479e54aad960d72cf Author: Ian Dunn <du...@gnu.org> Commit: Ian Dunn <du...@gnu.org>
Added tests for cache * org-edna.el (org-edna--get-cache-entry): New function for finding an entry in cache. (org-edna--handle-finder): Use it. (org-edna--expand-single-sexp-form): Update calling method of `org-edna--handle-finder'. * org-edna-tests.el (org-edna-cache/no-entry): (org-edna-cache/added-new-entry): (org-edna-cache/timed-out): New tests for cache. --- org-edna-tests.el | 71 ++++++++++++++++++++++++++++++++++++++++++++++++------- org-edna.el | 58 +++++++++++++++++++++++++++++---------------- 2 files changed, 100 insertions(+), 29 deletions(-) diff --git a/org-edna-tests.el b/org-edna-tests.el index 827e242..890ddd9 100644 --- a/org-edna-tests.el +++ b/org-edna-tests.el @@ -270,7 +270,7 @@ '(let ((targets1 nil) (consideration1 nil) (blocking-entry1 nil)) - (setq targets1 (org-edna--add-targets targets1 (org-edna--handle-finder 'org-edna-finder/self 'nil))) + (setq targets1 (org-edna--add-targets targets1 (org-edna--handle-finder 'org-edna-finder/self (quote)))) (setq blocking-entry1 (or blocking-entry1 (org-edna--handle-condition 'org-edna-condition/done? @@ -303,7 +303,7 @@ (blocking-entry2 blocking-entry1)) (setq targets2 (org-edna--add-targets targets2 - (org-edna--handle-finder 'org-edna-finder/match '("checklist")))) + (org-edna--handle-finder 'org-edna-finder/match '"checklist"))) (org-edna--handle-action 'org-edna-action/todo! targets2 (point-marker) @@ -313,7 +313,7 @@ (blocking-entry5 blocking-entry1)) (setq targets5 (org-edna--add-targets targets5 - (org-edna--handle-finder 'org-edna-finder/siblings 'nil))) + (org-edna--handle-finder 'org-edna-finder/siblings (quote)))) (org-edna--handle-action 'org-edna-action/todo! targets5 (point-marker) @@ -355,7 +355,7 @@ ;; Add targets for checklist match (setq targets3 (org-edna--add-targets targets3 - (org-edna--handle-finder 'org-edna-finder/match '("checklist")))) + (org-edna--handle-finder 'org-edna-finder/match '"checklist"))) ;; Handle condition (setq blocking-entry3 (or blocking-entry3 @@ -365,7 +365,7 @@ ;; Add targets for self finder (setq targets1 (org-edna--add-targets targets1 - (org-edna--handle-finder 'org-edna-finder/self 'nil))) + (org-edna--handle-finder 'org-edna-finder/self (quote)))) ;; Mark as TODO (org-edna--handle-action 'org-edna-action/todo! targets1 (point-marker) @@ -375,7 +375,7 @@ ;; Find siblings (setq targets1 (org-edna--add-targets targets1 - (org-edna--handle-finder 'org-edna-finder/siblings 'nil))) + (org-edna--handle-finder 'org-edna-finder/siblings (quote)))) ;; Mark as DONE (org-edna--handle-action 'org-edna-action/todo! targets1 (point-marker) @@ -416,7 +416,7 @@ ;; Add targets for checklist match (setq targets3 (org-edna--add-targets targets3 - (org-edna--handle-finder 'org-edna-finder/match '("checklist")))) + (org-edna--handle-finder 'org-edna-finder/match '"checklist"))) ;; Handle condition (setq blocking-entry3 (or blocking-entry3 @@ -426,7 +426,7 @@ ;; Add targets for self finder (setq targets1 (org-edna--add-targets targets1 - (org-edna--handle-finder 'org-edna-finder/self 'nil))) + (org-edna--handle-finder 'org-edna-finder/self (quote)))) ;; Mark as TODO (org-edna--handle-action 'org-edna-action/todo! targets1 (point-marker) @@ -1073,6 +1073,61 @@ (org-with-point-at current (org-edna-finder/relatives arg 'deadline-down size)))))) +(ert-deftest org-edna-cache/no-entry () + (let* ((org-edna-finder-use-cache t) + (org-edna--finder-cache (make-hash-table :test 'equal))) + ;; Empty, so `org-edna--get-cache-entry' should return nil. + (should (not (org-edna--get-cache-entry 'org-edna-finder/match '("test&1")))))) + +(ert-deftest org-edna-cache/added-new-entry () + (let* ((org-edna-finder-use-cache t) + (org-edna--finder-cache (make-hash-table :test 'equal)) + (org-agenda-files `(,org-edna-test-file)) + (targets (org-edna--handle-finder 'org-edna-finder/match "test&1"))) + (should (= (length targets) 2)) + (should (string-equal (org-edna-heading (nth 0 targets)) "Tagged Heading 1")) + (should (string-equal (org-edna-heading (nth 1 targets)) "Tagged Heading 2")) + (should (= (hash-table-count org-edna--finder-cache) 1)) + ;; Verify that we've got a valid cache entry. + (should (org-edna--get-cache-entry 'org-edna-finder/match '("test&1"))) + ;; Verify that any other signature returns nil. + (should (not (org-edna--get-cache-entry 'org-edna-finder/match '("test&2")))) + (let ((cache-entry (gethash (make-org-edna--finder-input :func-sym 'org-edna-finder/match + :args '("test&1")) + org-edna--finder-cache))) + (should cache-entry) + (should (equal (org-edna--finder-cache-entry-input cache-entry) + (make-org-edna--finder-input :func-sym 'org-edna-finder/match + :args '("test&1")))) + (should (equal (org-edna--finder-cache-entry-results cache-entry) + targets))))) + +(ert-deftest org-edna-cache/timed-out () + (let* ((org-edna-finder-use-cache t) + (org-edna--finder-cache (make-hash-table :test 'equal)) + (org-edna-finder-cache-timeout 1) ;; Set timeout to 1 second + (org-agenda-files `(,org-edna-test-file)) + (targets (org-edna--handle-finder 'org-edna-finder/match "test&1")) + ;; Time increment required to invalidate a cache entry + (time-increment `(0 ,org-edna-finder-cache-timeout))) + (should (org-edna--get-cache-entry 'org-edna-finder/match '("test&1"))) + ;; Validate the cache entry + (let ((cache-entry (gethash (make-org-edna--finder-input :func-sym 'org-edna-finder/match + :args '("test&1")) + org-edna--finder-cache))) + (should cache-entry) + (should (equal (org-edna--finder-cache-entry-input cache-entry) + (make-org-edna--finder-input :func-sym 'org-edna-finder/match + :args '("test&1")))) + (should (equal (org-edna--finder-cache-entry-results cache-entry) + targets)) + ;; Override `current-time' so we can get a deterministic value + ;; The value invalidates the cache entry + (cl-letf* (((symbol-function 'current-time) + (lambda () (time-add (org-edna--finder-cache-entry-last-run-time cache-entry) + time-increment)))) + (should (not (org-edna--get-cache-entry 'org-edna-finder/match '("test&1")))))))) + ;; Actions diff --git a/org-edna.el b/org-edna.el index 773fd74..5a152e6 100644 --- a/org-edna.el +++ b/org-edna.el @@ -387,7 +387,7 @@ correspond to internal variables." (`(,type . ,func) (org-edna--function-for-key key))) (pcase type ('finder - `(setq ,target-var (org-edna--add-targets ,target-var (org-edna--handle-finder ',func ',args)))) + `(setq ,target-var (org-edna--add-targets ,target-var (org-edna--handle-finder ',func ',@args)))) ('action `(org-edna--handle-action ',func ,target-var (point-marker) ',args)) ('condition @@ -506,35 +506,51 @@ adding unrelated headlines, will be taken into account." (entry (make-org-edna--finder-cache-entry :input input :results results :last-run-time (current-time)))) - (map-put org-edna--finder-cache input entry))) + (puthash input entry org-edna--finder-cache) + ;; Returning the results here passes them to the calling function. It's the + ;; only part of the entry we care about here. + results)) (defun org-edna--finder-cache-timeout (_func-sym) ;; In the future, we may want to support configurable timeouts on a per-finder ;; basis. org-edna-finder-cache-timeout) -(defun org-edna--handle-finder (func-sym args) +(defun org-edna--get-cache-entry (func-sym args) + "Find a valid entry in the cache. + +If none exists, return nil. An entry is invalid for any of the +following reasons: + +- It doesn't exist +- It has timed out +- It contains an invalid marker" + (let* ((input (make-org-edna--finder-input :func-sym func-sym + :args args)) + (entry (gethash input org-edna--finder-cache))) + (cond + ;; If we don't have an entry, rerun and make a new one. + ((not entry) nil) + ;; If we do have an entry, but it's timed out, then create a new one. + ((>= (float-time (time-subtract (current-time) + (org-edna--finder-cache-entry-last-run-time entry))) + (org-edna--finder-cache-timeout func-sym)) + nil) + ;; If any element of the results is an invalid marker, then rerun. + ((seq-find (lambda (x) (not (markerp x))) (org-edna--finder-cache-entry-results entry) nil) + nil) + ;; We have an entry created within the allowed interval. + (t entry)))) + +(defun org-edna--handle-finder (func-sym &rest args) (if (not org-edna-finder-use-cache) ;; Not using cache, so use the function directly. (apply func-sym args) - (let* ((input (make-org-edna--finder-input :func-sym func-sym - :args args)) - (entry (map-elt org-edna--finder-cache input))) - (cond - ;; If we don't have an entry, rerun and make a new one. - ((not entry) - (org-edna--add-to-finder-cache func-sym args)) - ;; If we do have an entry, but it's timed out, then create a new one. - ((>= (float-time (time-subtract (current-time) - (org-edna--finder-cache-entry-last-run-time entry))) - (org-edna--finder-cache-timeout func-sym)) - (org-edna--add-to-finder-cache func-sym args)) - ;; If any element of the results is an invalid marker, then rerun. - ((seq-find (lambda (x) (not (markerp x))) (org-edna--finder-cache-entry-results entry) nil) - (org-edna--add-to-finder-cache func-sym args)) - ;; We have an entry created within the allowed interval. - (t - (org-edna--finder-cache-entry-results entry)))))) + (let* ((entry (org-edna--get-cache-entry func-sym args))) + (if entry + (org-edna--finder-cache-entry-results entry) + ;; Adds the entry to the cache, and returns the results. + (org-edna--add-to-finder-cache func-sym args)))))