diff options
| author | Craig Jennings <c@cjennings.net> | 2026-05-05 04:59:46 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-05-05 04:59:46 -0500 |
| commit | 27f6f3087a3191200d657c94762984f10dcf89ca (patch) | |
| tree | 0f0bfd488c4e51ff1c427ee05a59f405f4bb6dbb | |
| parent | e09e001f7c6fb95074414facae64e01515c7a82c (diff) | |
| download | org-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.
| -rw-r--r-- | tests/test-org-drill-entries-loop.el | 130 |
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 |
