aboutsummaryrefslogtreecommitdiff
path: root/tests/test-org-drill-reschedule.el
blob: b05fd83656cbf21952aa1073f5ef613fe9b02eba (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
131
132
133
134
135
136
137
138
139
140
141
142
143
;;; test-org-drill-reschedule.el --- Tests for org-drill-reschedule rating loop  -*- lexical-binding: t; -*-

;;; Commentary:
;; Tests for the rating loop in `org-drill-reschedule', the function
;; that prompts the user to rate their recall and schedules the next
;; review.  Drives the loop via mocked `read-key-sequence' returning
;; specific keystrokes.
;;
;; Mock pattern: replace `read-key-sequence' with a stub that returns
;; the desired key string.  The function then walks its rating cond
;; and calls `org-drill-smart-reschedule' just like in production.

;;; 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\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))

(defmacro with-key-input (key &rest body)
  "Run BODY with `read-key-sequence' returning KEY (a string)."
  (declare (indent 1))
  `(cl-letf (((symbol-function 'read-key-sequence)
              (lambda (_prompt) ,key))
             ((symbol-function 'sit-for) #'ignore))
     ,@body))

;;;; Quality 0..5 returns the integer rating

(ert-deftest test-org-drill-reschedule-quality-5-returns-5 ()
  "Pressing `5' returns the integer 5 — the user-visible quality rating."
  (with-fresh-drill-entry
    (with-fixed-now
      (with-key-input "5"
        (let ((session (org-drill-session)))
          (should (= 5 (org-drill-reschedule session))))))))

(ert-deftest test-org-drill-reschedule-quality-0-returns-0 ()
  (with-fresh-drill-entry
    (with-fixed-now
      (with-key-input "0"
        (let ((session (org-drill-session)))
          (should (= 0 (org-drill-reschedule session))))))))

(ert-deftest test-org-drill-reschedule-quality-3-returns-3 ()
  (with-fresh-drill-entry
    (with-fixed-now
      (with-key-input "3"
        (let ((session (org-drill-session)))
          (should (= 3 (org-drill-reschedule session))))))))

;;;; Quit / edit non-numeric returns

(ert-deftest test-org-drill-reschedule-quit-key-returns-nil ()
  "Pressing the configured quit key returns nil."
  (with-fresh-drill-entry
    (with-fixed-now
      (with-key-input (string org-drill--quit-key)
        (let ((session (org-drill-session)))
          (should (null (org-drill-reschedule session))))))))

(ert-deftest test-org-drill-reschedule-edit-key-returns-edit ()
  "Pressing the configured edit key returns the symbol `edit'."
  (with-fresh-drill-entry
    (with-fixed-now
      (with-key-input (string org-drill--edit-key)
        (let ((session (org-drill-session)))
          (should (eq 'edit (org-drill-reschedule session))))))))

;;;; Side effects on the entry

(ert-deftest test-org-drill-reschedule-rating-records-quality-in-session ()
  "After a rating, the quality is pushed onto the session's qualities slot."
  (with-fresh-drill-entry
    (with-fixed-now
      (with-key-input "4"
        (let ((session (org-drill-session)))
          (org-drill-reschedule session)
          (should (member 4 (oref session qualities))))))))

(ert-deftest test-org-drill-reschedule-rating-schedules-next-review ()
  "After a non-failure rating, the entry has a SCHEDULED stamp."
  (with-fresh-drill-entry
    (with-fixed-now
      (with-key-input "5"
        (let ((session (org-drill-session)))
          (org-drill-reschedule session)
          (should (org-entry-get (point) "SCHEDULED")))))))

(ert-deftest test-org-drill-reschedule-cram-mode-doesnt-reschedule ()
  "Cram mode skips the reschedule — only ratings are recorded."
  (with-fresh-drill-entry
    (with-fixed-now
      (with-key-input "5"
        (let ((session (org-drill-session)))
          (oset session cram-mode t)
          (org-drill-reschedule session)
          ;; No SCHEDULED was set in cram mode.
          (should (null (org-entry-get (point) "SCHEDULED"))))))))

;;;; Failure threshold → leech tagging

(ert-deftest test-org-drill-reschedule-failure-over-threshold-tags-leech ()
  "Many failures (>= leech-failure-threshold) flips the entry to a leech."
  (with-fresh-drill-entry
    (org-set-property "DRILL_FAILURE_COUNT" "5")        ; 5 prior failures
    (with-fixed-now
      (with-key-input "0"
        (let ((session (org-drill-session))
              (org-drill-leech-failure-threshold 3))
          (org-drill-reschedule session)
          (should (member "leech" (org-get-tags nil t))))))))

(ert-deftest test-org-drill-reschedule-failure-under-threshold-no-leech ()
  "A handful of failures (< threshold) doesn't mark the entry leech."
  (with-fresh-drill-entry
    (org-set-property "DRILL_FAILURE_COUNT" "0")
    (with-fixed-now
      (with-key-input "0"
        (let ((session (org-drill-session))
              (org-drill-leech-failure-threshold 5))
          (org-drill-reschedule session)
          (should-not (member "leech" (org-get-tags nil t))))))))

(provide 'test-org-drill-reschedule)

;;; test-org-drill-reschedule.el ends here