aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-05-05 14:06:48 -0500
committerCraig Jennings <c@cjennings.net>2026-05-05 14:06:48 -0500
commit0d32f8f6df9406c12306d97592446408ba7984e3 (patch)
treebecff5fbd172082fba54ed95a20f3133918c4118
parent63713acf8238aceee199a5f595fda6470c633939 (diff)
downloadorg-drill-0d32f8f6df9406c12306d97592446408ba7984e3.tar.gz
org-drill-0d32f8f6df9406c12306d97592446408ba7984e3.zip
test: cover map-leitner-capture, response-mode, timer, sm2/simple8 dispatch
I added direct tests for `org-drill-map-leitner-capture' (route by DRILL_LEITNER_BOX), the response-mode exit-kind handlers (quit/edit/skip/ tags/rtn), `org-drill-presentation-timer-cancel', and the sm2/simple8 branches of `org-drill-smart-reschedule''s algorithm dispatcher. Coverage moved from 80.6% to 81.8%.
-rw-r--r--tests/test-org-drill-map-leitner-capture.el84
-rw-r--r--tests/test-org-drill-response-and-timer.el108
-rw-r--r--tests/test-org-drill-smart-reschedule.el18
3 files changed, 210 insertions, 0 deletions
diff --git a/tests/test-org-drill-map-leitner-capture.el b/tests/test-org-drill-map-leitner-capture.el
new file mode 100644
index 0000000..bc43c7d
--- /dev/null
+++ b/tests/test-org-drill-map-leitner-capture.el
@@ -0,0 +1,84 @@
+;;; test-org-drill-map-leitner-capture.el --- Tests for map-leitner-capture -*- lexical-binding: t; -*-
+
+;;; Commentary:
+;; Tests for `org-drill-map-leitner-capture', the per-entry callback that
+;; routes a leitner-tagged heading into either `org-drill-leitner-boxed-entries'
+;; (when DRILL_LEITNER_BOX is set 0-5) or `org-drill-leitner-unboxed-entries'
+;; (when the property is missing). Box values >5 are graduates and are
+;; ignored.
+
+;;; Code:
+
+(require 'ert)
+(require 'cl-lib)
+(require 'org)
+(require 'org-drill)
+
+(defmacro with-leitner-tempfile (content &rest body)
+ "Run BODY in a tempfile-backed org buffer with CONTENT, with empty leitner queues."
+ (declare (indent 1))
+ `(let ((tmpfile (make-temp-file "org-drill-leitner-test-" nil ".org"))
+ (org-drill-leitner-boxed-entries nil)
+ (org-drill-leitner-unboxed-entries nil))
+ (unwind-protect
+ (with-current-buffer (find-file-noselect tmpfile)
+ (let ((org-startup-folded nil))
+ (insert ,content)
+ (org-mode)
+ (goto-char (point-min))
+ ,@body))
+ (when (file-exists-p tmpfile) (delete-file tmpfile)))))
+
+(ert-deftest test-map-leitner-capture-no-box-property-pushes-unboxed ()
+ "Entry with no DRILL_LEITNER_BOX lands in the unboxed queue."
+ (with-leitner-tempfile "* Card :leitner:\nbody\n"
+ (let ((session (org-drill-session))
+ (org-drill-question-tag org-drill-leitner-tag))
+ (cl-letf (((symbol-function 'org-drill-progress-message) #'ignore))
+ (org-drill-map-leitner-capture session))
+ (should (= 1 (length org-drill-leitner-unboxed-entries)))
+ (should (= 0 (length org-drill-leitner-boxed-entries))))))
+
+(ert-deftest test-map-leitner-capture-box-in-range-pushes-boxed ()
+ "Entry with DRILL_LEITNER_BOX 0-5 lands in the boxed queue."
+ (dolist (box '("0" "1" "2" "3" "4" "5"))
+ (with-leitner-tempfile (format "* Card :leitner:\n:PROPERTIES:\n:DRILL_LEITNER_BOX: %s\n:END:\nbody\n" box)
+ (let ((session (org-drill-session))
+ (org-drill-question-tag org-drill-leitner-tag))
+ (cl-letf (((symbol-function 'org-drill-progress-message) #'ignore))
+ (org-drill-map-leitner-capture session))
+ (should (= 1 (length org-drill-leitner-boxed-entries)))
+ (should (= 0 (length org-drill-leitner-unboxed-entries)))))))
+
+(ert-deftest test-map-leitner-capture-graduate-box-skipped ()
+ "Entry with DRILL_LEITNER_BOX > 5 is ignored — already graduated."
+ (with-leitner-tempfile "* Card :leitner:\n:PROPERTIES:\n:DRILL_LEITNER_BOX: 6\n:END:\nbody\n"
+ (let ((session (org-drill-session))
+ (org-drill-question-tag org-drill-leitner-tag))
+ (cl-letf (((symbol-function 'org-drill-progress-message) #'ignore))
+ (org-drill-map-leitner-capture session))
+ (should (= 0 (length org-drill-leitner-boxed-entries)))
+ (should (= 0 (length org-drill-leitner-unboxed-entries))))))
+
+(ert-deftest test-map-leitner-capture-non-drill-entry-skipped ()
+ "Non-drill heading is skipped entirely."
+ (with-leitner-tempfile "Just text, no headlines\n"
+ (let ((session (org-drill-session))
+ (org-drill-question-tag org-drill-leitner-tag))
+ (cl-letf (((symbol-function 'org-drill-progress-message) #'ignore))
+ (org-drill-map-leitner-capture session))
+ (should (= 0 (length org-drill-leitner-boxed-entries)))
+ (should (= 0 (length org-drill-leitner-unboxed-entries))))))
+
+(ert-deftest test-map-leitner-capture-increments-session-counter ()
+ "Each call bumps the session's `cnt' slot."
+ (with-leitner-tempfile "* Card :leitner:\nbody\n"
+ (let ((session (org-drill-session)))
+ (oset session cnt 0)
+ (cl-letf (((symbol-function 'org-drill-progress-message) #'ignore))
+ (org-drill-map-leitner-capture session)
+ (org-drill-map-leitner-capture session))
+ (should (= 2 (oref session cnt))))))
+
+(provide 'test-org-drill-map-leitner-capture)
+;;; test-org-drill-map-leitner-capture.el ends here
diff --git a/tests/test-org-drill-response-and-timer.el b/tests/test-org-drill-response-and-timer.el
new file mode 100644
index 0000000..4da8e9a
--- /dev/null
+++ b/tests/test-org-drill-response-and-timer.el
@@ -0,0 +1,108 @@
+;;; test-org-drill-response-and-timer.el --- Tests for response-mode and timer -*- lexical-binding: t; -*-
+
+;;; Commentary:
+;; Tests for the response-mode helpers and the presentation-timer cancel
+;; helper. These are all small functions whose only job is to flip a
+;; session slot or clear a global timer.
+
+;;; Code:
+
+(require 'ert)
+(require 'cl-lib)
+(require 'org)
+(require 'org-drill)
+
+;;;; org-drill-presentation-timer-cancel
+
+(ert-deftest test-presentation-timer-cancel-with-no-timer-clears-counter ()
+ "Cancel called with no timer just resets the counter."
+ (let ((org-drill-presentation-timer nil)
+ (org-drill-presentation-timer-calls 7))
+ (org-drill-presentation-timer-cancel)
+ (should (null org-drill-presentation-timer))
+ (should (= 0 org-drill-presentation-timer-calls))))
+
+(ert-deftest test-presentation-timer-cancel-with-active-timer-cancels-and-clears ()
+ "Cancel called with an active timer cancels it and zeroes the counter."
+ (let ((cancelled nil))
+ (cl-letf (((symbol-function 'cancel-timer)
+ (lambda (_) (setq cancelled t))))
+ (let ((org-drill-presentation-timer 'fake-timer)
+ (org-drill-presentation-timer-calls 5))
+ (org-drill-presentation-timer-cancel)
+ (should cancelled)
+ (should (null org-drill-presentation-timer))
+ (should (= 0 org-drill-presentation-timer-calls))))))
+
+;;;; Response-mode exit kinds
+
+(defmacro with-fake-response-buffer (&rest body)
+ "Run BODY in a buffer where org-drill-current-session is bound and
+exit-recursive-edit and kill-buffer are stubbed."
+ (declare (indent 0))
+ `(let ((session (org-drill-session))
+ (exited nil))
+ (with-temp-buffer
+ (setq-local org-drill-current-session session)
+ (cl-letf (((symbol-function 'kill-buffer) #'ignore)
+ ((symbol-function 'exit-recursive-edit)
+ (lambda () (setq exited t))))
+ ,@body)
+ (cons session exited))))
+
+(ert-deftest test-response-quit-sets-exit-kind-quit ()
+ (let* ((result (with-fake-response-buffer
+ (org-drill-response-quit)))
+ (session (car result)))
+ (should (eq 'quit (oref session exit-kind)))
+ (should (cdr result))))
+
+(ert-deftest test-response-edit-sets-exit-kind-edit ()
+ (let* ((result (with-fake-response-buffer
+ (org-drill-response-edit)))
+ (session (car result)))
+ (should (eq 'edit (oref session exit-kind)))
+ (should (cdr result))))
+
+(ert-deftest test-response-skip-sets-exit-kind-skip ()
+ (let* ((result (with-fake-response-buffer
+ (org-drill-response-skip)))
+ (session (car result)))
+ (should (eq 'skip (oref session exit-kind)))
+ (should (cdr result))))
+
+(ert-deftest test-response-tags-sets-exit-kind-tags ()
+ (let* ((result (with-fake-response-buffer
+ (org-drill-response-tags)))
+ (session (car result)))
+ (should (eq 'tags (oref session exit-kind)))
+ (should (cdr result))))
+
+(ert-deftest test-response-rtn-stores-typed-answer-and-exits ()
+ "Return key captures buffer text into typed-answer and sets exit-kind t."
+ (let ((session (org-drill-session))
+ (exited nil))
+ (with-temp-buffer
+ (insert "my-answer")
+ (setq-local org-drill-current-session session)
+ (cl-letf (((symbol-function 'kill-buffer) #'ignore)
+ ((symbol-function 'exit-recursive-edit)
+ (lambda () (setq exited t))))
+ (org-drill-response-rtn)))
+ (should (equal "my-answer" (oref session typed-answer)))
+ (should (eq t (oref session exit-kind)))
+ (should exited)))
+
+;;;; Response buffer creation
+
+(ert-deftest test-response-get-buffer-create-returns-fresh-buffer ()
+ "The helper returns a buffer in `org-drill-response-mode' with empty content."
+ (let ((buf (org-drill-response-get-buffer-create)))
+ (unwind-protect
+ (with-current-buffer buf
+ (should (eq major-mode 'org-drill-response-mode))
+ (should (= (point-min) (point-max))))
+ (kill-buffer buf))))
+
+(provide 'test-org-drill-response-and-timer)
+;;; test-org-drill-response-and-timer.el ends here
diff --git a/tests/test-org-drill-smart-reschedule.el b/tests/test-org-drill-smart-reschedule.el
index 49eddb0..6fbf3d6 100644
--- a/tests/test-org-drill-smart-reschedule.el
+++ b/tests/test-org-drill-smart-reschedule.el
@@ -91,6 +91,24 @@ because the cond compared nil with `=' before the type-guard."
(let ((scheduled (current-scheduled-time-string)))
(should scheduled)))))
+;;;; Algorithm dispatch (covers the cl-case branches in smart-reschedule)
+
+(ert-deftest test-org-drill-smart-reschedule-sm2-algorithm-schedules ()
+ "With sm2 selected, smart-reschedule still produces a SCHEDULED stamp."
+ (with-fresh-drill-entry
+ (with-fixed-now
+ (let ((org-drill-spaced-repetition-algorithm 'sm2))
+ (org-drill-smart-reschedule 5))
+ (should (current-scheduled-time-string)))))
+
+(ert-deftest test-org-drill-smart-reschedule-simple8-algorithm-schedules ()
+ "With simple8 selected, smart-reschedule still produces a SCHEDULED stamp."
+ (with-fresh-drill-entry
+ (with-fixed-now
+ (let ((org-drill-spaced-repetition-algorithm 'simple8))
+ (org-drill-smart-reschedule 5))
+ (should (current-scheduled-time-string)))))
+
;;;; Property side-effects
(ert-deftest test-org-drill-smart-reschedule-writes-drill-properties ()