aboutsummaryrefslogtreecommitdiff
path: root/tests/test-org-drill-entries-loop.el
blob: 9a0dfedf569701c7f3925cf632ee2f05c7a31bab (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
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