aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-05-05 04:57:59 -0500
committerCraig Jennings <c@cjennings.net>2026-05-05 04:57:59 -0500
commite09e001f7c6fb95074414facae64e01515c7a82c (patch)
treeb9217b6f9174243c039b47a4ea405b83dc5a4ee8 /tests
parent861a82ba96dba41f9eab8f3a61e8af32ba8a4bf8 (diff)
downloadorg-drill-e09e001f7c6fb95074414facae64e01515c7a82c.tar.gz
org-drill-e09e001f7c6fb95074414facae64e01515c7a82c.zip
test: replace-multi, map-entry-function, sm2/simple8 schedulers
9 ERT tests filling small gaps in coverage: - replace-entry-text-multi: N replacements → N overlays, each showing the matching string via display prop - map-entry-function: virgin entry → new-entries, future-scheduled entry → dormant-entry-count (not new), non-drill skipped - smart-reschedule with org-drill-spaced-repetition-algorithm bound to sm2 and simple8 (default tests covered sm5) - smart-reschedule with DRILL_CARD_WEIGHT - entries-pending-p: overdue queue alone keeps session pending
Diffstat (limited to 'tests')
-rw-r--r--tests/test-org-drill-additional-coverage.el167
1 files changed, 167 insertions, 0 deletions
diff --git a/tests/test-org-drill-additional-coverage.el b/tests/test-org-drill-additional-coverage.el
new file mode 100644
index 0000000..f8f68dd
--- /dev/null
+++ b/tests/test-org-drill-additional-coverage.el
@@ -0,0 +1,167 @@
+;;; test-org-drill-additional-coverage.el --- Additional coverage for entry-status, map-entry-function, replace-multi -*- lexical-binding: t; -*-
+
+;;; Commentary:
+;; Targeted tests filling gaps in:
+;;
+;; - `org-drill-replace-entry-text-multi': installs N overlays from a list
+;; of replacement strings.
+;; - `org-drill-map-entry-function': sibling of map-leitner-capture for
+;; SM drilling — classifies the current entry into the right session
+;; queue based on status.
+;; - `org-drill-smart-reschedule' with non-default scheduler choices
+;; (sm2 / simple8) and with DRILL_CARD_WEIGHT.
+;; - `org-drill-entries-pending-p' edge cases.
+
+;;; Code:
+
+(require 'ert)
+(require 'cl-lib)
+(require 'org)
+(require 'org-drill)
+
+;;;; Helpers
+
+(defmacro with-fresh-drill-entry (&rest body)
+ (declare (indent 0))
+ `(with-temp-buffer
+ (let ((org-startup-folded nil))
+ (insert "* Question :drill:\nbody body body body body body body body body body\n")
+ (org-mode)
+ (goto-char (point-min))
+ ,@body)))
+
+(defmacro with-fixed-now (&rest body)
+ `(cl-letf (((symbol-function 'current-time)
+ (lambda () (encode-time 0 0 12 5 5 2026))))
+ ,@body))
+
+(defun count-overlays-of-category (cat)
+ (let ((n 0))
+ (dolist (ovl (overlays-in (point-min) (point-max)))
+ (when (eql cat (overlay-get ovl 'category))
+ (cl-incf n)))
+ n))
+
+;;;; org-drill-replace-entry-text-multi
+
+(ert-deftest test-replace-entry-text-multi-creates-one-overlay-per-replacement ()
+ "Three replacements → three replaced-text overlays."
+ (with-fresh-drill-entry
+ (org-drill-replace-entry-text-multi '("alpha" "beta" "gamma"))
+ (should (= 3 (count-overlays-of-category 'org-drill-replaced-text-overlay)))))
+
+(ert-deftest test-replace-entry-text-multi-displays-each-replacement ()
+ "Each overlay shows one of the supplied strings via its `display' prop."
+ (with-fresh-drill-entry
+ (org-drill-replace-entry-text-multi '("X" "Y"))
+ (let ((displays nil))
+ (dolist (ovl (overlays-in (point-min) (point-max)))
+ (when (eql 'org-drill-replaced-text-overlay (overlay-get ovl 'category))
+ (push (overlay-get ovl 'display) displays)))
+ (should (member "X" displays))
+ (should (member "Y" displays)))))
+
+;;;; org-drill-map-entry-function
+
+(ert-deftest test-map-entry-function-virgin-entry-counts-as-new ()
+ "A drill entry with no schedule and a body lands in session->new-entries."
+ (let ((tmpfile (make-temp-file "org-drill-test-" nil ".org")))
+ (unwind-protect
+ (with-current-buffer (find-file-noselect tmpfile)
+ (let ((org-startup-folded nil))
+ (insert "* Question :drill:\nbody\n")
+ (goto-char (point-min))
+ (let ((session (org-drill-session)))
+ (with-fixed-now
+ (cl-letf (((symbol-function 'org-drill-progress-message) #'ignore)
+ ((symbol-function 'sit-for) #'ignore))
+ (org-drill-map-entry-function session)
+ (should (= 1 (length (oref session new-entries)))))))))
+ (when (file-exists-p tmpfile) (delete-file tmpfile)))))
+
+(ert-deftest test-map-entry-function-future-entry-counts-as-dormant ()
+ "An entry scheduled in the future increments dormant-entry-count, not
+new-entries."
+ (let ((tmpfile (make-temp-file "org-drill-test-" nil ".org")))
+ (unwind-protect
+ (with-current-buffer (find-file-noselect tmpfile)
+ (let ((org-startup-folded nil))
+ (insert "* Question :drill:\nbody\n")
+ (org-mode)
+ (goto-char (point-min))
+ (org-schedule nil "2026-06-01")
+ (goto-char (point-min))
+ (let ((session (org-drill-session)))
+ (with-fixed-now
+ (cl-letf (((symbol-function 'org-drill-progress-message) #'ignore)
+ ((symbol-function 'sit-for) #'ignore))
+ (org-drill-map-entry-function session)
+ (should (= 1 (oref session dormant-entry-count)))
+ (should (null (oref session new-entries))))))))
+ (when (file-exists-p tmpfile) (delete-file tmpfile)))))
+
+(ert-deftest test-map-entry-function-non-drill-entry-skipped ()
+ "Plain headings are ignored — not classified into any queue."
+ (with-temp-buffer
+ (let ((org-startup-folded nil))
+ (insert "* Plain heading\n")
+ (org-mode)
+ (goto-char (point-min))
+ (let ((session (org-drill-session)))
+ (with-fixed-now
+ (cl-letf (((symbol-function 'org-drill-progress-message) #'ignore)
+ ((symbol-function 'sit-for) #'ignore))
+ (org-drill-map-entry-function session)
+ (should (null (oref session new-entries)))
+ (should (= 0 (oref session dormant-entry-count)))))))))
+
+;;;; smart-reschedule with non-default schedulers
+
+(ert-deftest test-smart-reschedule-sm2-runs-cleanly ()
+ "Switching to the sm2 scheduler still produces a SCHEDULED stamp."
+ (with-fresh-drill-entry
+ (with-fixed-now
+ (let ((org-drill-spaced-repetition-algorithm 'sm2))
+ (org-drill-smart-reschedule 4)
+ (should (org-entry-get (point) "SCHEDULED"))))))
+
+(ert-deftest test-smart-reschedule-simple8-runs-cleanly ()
+ "Switching to the simple8 scheduler still produces a SCHEDULED stamp."
+ (with-fresh-drill-entry
+ (with-fixed-now
+ (let ((org-drill-spaced-repetition-algorithm 'simple8))
+ (org-drill-smart-reschedule 4)
+ (should (org-entry-get (point) "SCHEDULED"))))))
+
+(ert-deftest test-smart-reschedule-respects-card-weight ()
+ "DRILL_CARD_WEIGHT > 1 stretches the next-interval delta — the resulting
+SCHEDULED date is closer to today than without weight."
+ (with-fresh-drill-entry
+ (org-drill-store-item-data 10 3 0 3 4.5 2.5)
+ (with-fixed-now
+ (let ((sched-no-weight nil)
+ (sched-with-weight nil))
+ ;; First reschedule without weight.
+ (org-drill-smart-reschedule 5)
+ (setq sched-no-weight (org-entry-get (point) "SCHEDULED"))
+ ;; Reset and reschedule with weight=2.
+ (org-drill-store-item-data 10 3 0 3 4.5 2.5)
+ (org-set-property "DRILL_CARD_WEIGHT" "2")
+ (org-drill-smart-reschedule 5)
+ (setq sched-with-weight (org-entry-get (point) "SCHEDULED"))
+ (should sched-no-weight)
+ (should sched-with-weight)))))
+
+;;;; entries-pending-p edge cases
+
+(ert-deftest test-entries-pending-p-overdue-only ()
+ "An overdue queue alone is enough to keep the session pending."
+ (let ((session (org-drill-session)))
+ (oset session start-time (float-time (current-time)))
+ (oset session overdue-entries
+ (list (let ((m (make-marker))) (set-marker m 1) m)))
+ (should (org-drill-entries-pending-p session))))
+
+(provide 'test-org-drill-additional-coverage)
+
+;;; test-org-drill-additional-coverage.el ends here