aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-05-05 04:59:46 -0500
committerCraig Jennings <c@cjennings.net>2026-05-05 04:59:46 -0500
commit27f6f3087a3191200d657c94762984f10dcf89ca (patch)
tree0f0bfd488c4e51ff1c427ee05a59f405f4bb6dbb /tests
parente09e001f7c6fb95074414facae64e01515c7a82c (diff)
downloadorg-drill-27f6f3087a3191200d657c94762984f10dcf89ca.tar.gz
org-drill-27f6f3087a3191200d657c94762984f10dcf89ca.zip
test: org-drill-entries main loop queue-routing logic
5 ERT tests for the session loop body: - quit return (nil) sets end-pos = :quit and exits the loop - edit return ('edit) sets end-pos to a marker and exits - passing rating (>failure-quality) routes marker to done-entries - failing rating (<= failure-quality) skips done-entries - skip return clears current-item without queueing Tests use a tempfile-backed buffer because pop-next-pending-entry calls org-drill-entry-p on each marker, which requires real org buffer state.
Diffstat (limited to 'tests')
-rw-r--r--tests/test-org-drill-entries-loop.el130
1 files changed, 130 insertions, 0 deletions
diff --git a/tests/test-org-drill-entries-loop.el b/tests/test-org-drill-entries-loop.el
new file mode 100644
index 0000000..9a0dfed
--- /dev/null
+++ b/tests/test-org-drill-entries-loop.el
@@ -0,0 +1,130 @@
+;;; test-org-drill-entries-loop.el --- Tests for org-drill-entries main loop -*- lexical-binding: t; -*-
+
+;;; Commentary:
+;; Tests for `org-drill-entries' — the main session loop that walks
+;; through pending cards and routes the user's rating into the right
+;; queue (done / again).
+;;
+;; The loop calls `org-drill-entry' on each card; we mock it to return
+;; controlled values (nil = quit, 'edit, 'skip, 0..5 = rating) so the
+;; test can verify the queue-routing logic without driving a real
+;; interactive prompt.
+
+;;; Code:
+
+(require 'ert)
+(require 'cl-lib)
+(require 'org)
+(require 'org-drill)
+
+;;;; Helpers
+
+(defmacro with-org-drill-tempfile (content &rest body)
+ "Run BODY in a tempfile-backed org buffer with CONTENT.
+Markers used for queue testing must point to real headings in a real
+file because pop-next-pending-entry calls org-drill-entry-p on each."
+ (declare (indent 1))
+ `(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 ,content)
+ (org-mode)
+ (goto-char (point-min))
+ ,@body))
+ (when (file-exists-p tmpfile) (delete-file tmpfile)))))
+
+(defun heading-marker (regex)
+ "Make a marker at the start of the first heading matching REGEX."
+ (save-excursion
+ (goto-char (point-min))
+ (re-search-forward regex nil t)
+ (line-beginning-position)
+ (let ((m (make-marker))) (set-marker m (line-beginning-position)) m)))
+
+;;;; Quit return → end-pos := :quit and loop exits
+
+(ert-deftest test-org-drill-entries-quit-stops-loop ()
+ "When org-drill-entry returns nil (quit), the loop exits and end-pos becomes :quit."
+ (with-org-drill-tempfile "* First :drill:\nbody one\n* Second :drill:\nbody two\n"
+ (let ((session (org-drill-session)))
+ (oset session start-time (float-time (current-time)))
+ (oset session new-entries
+ (list (heading-marker "^\\* First")
+ (heading-marker "^\\* Second")))
+ (cl-letf (((symbol-function 'org-drill-entry) (lambda (_) nil))
+ ((symbol-function 'sit-for) #'ignore))
+ (let ((result (org-drill-entries session)))
+ (should (null result))
+ (should (eq :quit (oref session end-pos))))))))
+
+;;;; Edit return → end-pos := point-marker, loop exits
+
+(ert-deftest test-org-drill-entries-edit-stops-loop-records-position ()
+ "When org-drill-entry returns 'edit, the loop exits and end-pos is a marker."
+ (with-org-drill-tempfile "* First :drill:\nbody one\n"
+ (let ((session (org-drill-session)))
+ (oset session start-time (float-time (current-time)))
+ (oset session new-entries (list (heading-marker "^\\* First")))
+ (cl-letf (((symbol-function 'org-drill-entry) (lambda (_) 'edit))
+ ((symbol-function 'sit-for) #'ignore))
+ (org-drill-entries session)
+ (should (markerp (oref session end-pos)))))))
+
+;;;; Successful rating → entry goes to done-entries, current-item cleared
+
+(ert-deftest test-org-drill-entries-passing-rating-pushes-to-done ()
+ "A passing rating (>failure-quality) routes the marker into done-entries."
+ (with-org-drill-tempfile "* First :drill:\nbody\n"
+ (let ((session (org-drill-session)))
+ (oset session start-time (float-time (current-time)))
+ (oset session new-entries (list (heading-marker "^\\* First")))
+ (cl-letf (((symbol-function 'org-drill-entry) (lambda (_) 5))
+ ((symbol-function 'sit-for) #'ignore))
+ (org-drill-entries session)
+ (should (= 1 (length (oref session done-entries))))
+ (should (null (oref session current-item)))))))
+
+;;;; Failure rating → entry goes to again-entries, not done
+
+(ert-deftest test-org-drill-entries-failing-rating-pushes-to-again ()
+ "A failing rating (<= failure-quality) routes the marker into again-entries."
+ (with-org-drill-tempfile "* First :drill:\nbody\n"
+ (let ((session (org-drill-session)))
+ (oset session start-time (float-time (current-time)))
+ (oset session new-entries (list (heading-marker "^\\* First")))
+ ;; First call returns 0 (fail), second returns nil to terminate
+ ;; the loop after the again-entry repeats.
+ (let ((calls 0))
+ (cl-letf (((symbol-function 'org-drill-entry)
+ (lambda (_)
+ (cl-incf calls)
+ (if (= calls 1) 0 nil)))
+ ((symbol-function 'sit-for) #'ignore))
+ (org-drill-entries session)
+ ;; The entry that was failed lives in again-entries (or got
+ ;; re-presented and then quit). Either way, done-entries is
+ ;; empty.
+ (should (null (oref session done-entries))))))))
+
+;;;; skip return → current-item nil-ed, marker not added to any queue
+
+(ert-deftest test-org-drill-entries-skip-clears-current-without-queueing ()
+ "Skip returns nil current-item, doesn't add to done or again."
+ (with-org-drill-tempfile "* First :drill:\nbody\n"
+ (let ((session (org-drill-session)))
+ (oset session start-time (float-time (current-time)))
+ (oset session new-entries (list (heading-marker "^\\* First")))
+ (let ((calls 0))
+ (cl-letf (((symbol-function 'org-drill-entry)
+ (lambda (_)
+ (cl-incf calls)
+ (if (= calls 1) 'skip nil)))
+ ((symbol-function 'sit-for) #'ignore))
+ (org-drill-entries session)
+ (should (null (oref session done-entries)))
+ (should (null (oref session again-entries))))))))
+
+(provide 'test-org-drill-entries-loop)
+
+;;; test-org-drill-entries-loop.el ends here