aboutsummaryrefslogtreecommitdiff
path: root/tests/test-org-drill-multicloze-hiding.el
blob: b2250088d7af6598305b5d1d28d323fd2aa8dd76 (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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
;;; test-org-drill-multicloze-hiding.el --- Tests for multicloze hide-n and hide-nth  -*- lexical-binding: t; -*-

;;; Commentary:
;; Tests for the multicloze presenters that hide a chosen subset of
;; cloze pieces.  With `org-drill-presentation-prompt' mocked, the
;; remaining work is the cloze-counting + position-selection logic,
;; which is the part we want under test.
;;
;; - `org-drill-present-multicloze-hide-n': hides N pieces (or shows
;;   only abs(N) when N is negative)
;; - `org-drill-present-multicloze-hide-nth': hides exactly the N-th
;;   piece (1-indexed; negative counts from the end)

;;; Code:

(require 'ert)
(require 'cl-lib)
(require 'org)
(require 'org-drill)

;;;; Helpers

(defmacro with-cloze-card (content &rest body)
  "Run BODY in a temp org buffer with CONTENT (a drill entry containing
clozes), point at start, fontification caches set up."
  (declare (indent 1))
  `(with-temp-buffer
     (let ((org-startup-folded nil))
       (insert ,content)
       (org-mode)
       (goto-char (point-min))
       (setq-local org-drill-cloze-regexp (org-drill--compute-cloze-regexp))
       ,@body)))

(defmacro with-mocked-prompt (return-value &rest body)
  "Run BODY with `org-drill-presentation-prompt' replaced by a stub
returning RETURN-VALUE.  Also stubs LaTeX preview / inline-images
helpers that aren't relevant in batch."
  (declare (indent 1))
  `(cl-letf (((symbol-function 'org-drill-presentation-prompt)
              (lambda (&rest _) ,return-value))
             ((symbol-function 'org-drill--show-latex-fragments) #'ignore)
             ((symbol-function 'org-display-inline-images) #'ignore))
     ,@body))

(defun count-cloze-overlays ()
  "Count the cloze-overlay-defaults overlays in the current buffer."
  (let ((n 0))
    (dolist (ovl (overlays-in (point-min) (point-max)))
      (when (eql 'org-drill-cloze-overlay-defaults
                 (overlay-get ovl 'category))
        (cl-incf n)))
    n))

(defvar overlays-during-prompt nil
  "Captured count of cloze overlays at the moment presentation-prompt fires.")

(defmacro with-mocked-prompt-capturing-overlays (&rest body)
  "Run BODY with presentation-prompt replaced by a stub that records
the cloze-overlay count at the moment it's called.  Records to
`overlays-during-prompt'."
  `(cl-letf (((symbol-function 'org-drill-presentation-prompt)
              (lambda (&rest _)
                (setq overlays-during-prompt (count-cloze-overlays))
                t))
             ((symbol-function 'org-drill--show-latex-fragments) #'ignore)
             ((symbol-function 'org-display-inline-images) #'ignore))
     (setq overlays-during-prompt nil)
     ,@body))

;;;; org-drill-present-multicloze-hide-n with positive N

(ert-deftest test-multicloze-hide-n-hides-exactly-n-cloze-pieces ()
  "With three cloze pieces and n=2, exactly 2 cloze overlays exist
at the moment the prompt fires."
  (with-cloze-card "* Question :drill:
The capitals are [Paris], [Rome], and [Madrid].
"
    (with-mocked-prompt-capturing-overlays
     (org-drill-present-multicloze-hide-n (org-drill-session) 2))
    (should (= 2 overlays-during-prompt))))

(ert-deftest test-multicloze-hide-n-hides-one-when-n-is-1 ()
  (with-cloze-card "* Question :drill:
[A] [B] [C] [D]
"
    (with-mocked-prompt-capturing-overlays
     (org-drill-present-multicloze-hide-n (org-drill-session) 1))
    (should (= 1 overlays-during-prompt))))

(ert-deftest test-multicloze-hide-n-no-cloze-no-overlays ()
  (with-cloze-card "* Question :drill:
No cloze syntax in this body.
"
    (with-mocked-prompt-capturing-overlays
     (org-drill-present-multicloze-hide-n (org-drill-session) 1))
    (should (= 0 overlays-during-prompt))))

;;;; org-drill-present-multicloze-hide-n with negative N (show-mode)

(ert-deftest test-multicloze-hide-n-negative-shows-only-n-pieces ()
  "n=-1 means \"show only 1\" — hide all but 1.  With 4 pieces, 3 hidden."
  (with-cloze-card "* Question :drill:
[A] [B] [C] [D]
"
    (with-mocked-prompt-capturing-overlays
     (org-drill-present-multicloze-hide-n (org-drill-session) -1))
    (should (= 3 overlays-during-prompt))))

(ert-deftest test-multicloze-hide-n-negative-2-shows-2-pieces ()
  "n=-2 means \"show 2\" — hide N-2 of N total."
  (with-cloze-card "* Question :drill:
[A] [B] [C] [D]
"
    (with-mocked-prompt-capturing-overlays
     (org-drill-present-multicloze-hide-n (org-drill-session) -2))
    (should (= 2 overlays-during-prompt))))

;;;; force-show-first / force-show-last interactions

(ert-deftest test-multicloze-hide-n-force-show-first-and-hide-first-mutually-exclusive ()
  "Passing both force-hide-first and force-show-first → user-visible error."
  (with-cloze-card "* Question :drill:
[A] [B] [C]
"
    (with-mocked-prompt t
      (should-error
       (org-drill-present-multicloze-hide-n (org-drill-session) 1 t nil t)))))

;;;; org-drill-present-multicloze-hide-nth — positive index

(ert-deftest test-multicloze-hide-nth-hides-only-the-nth-piece ()
  "Hides exactly the 2nd cloze piece, leaving the others visible.
Verified by checking the resulting overlay's bounds match the
2nd cloze."
  (with-cloze-card "* Question :drill:
[A] [B] [C]
"
    (with-mocked-prompt-capturing-overlays
     (org-drill-present-multicloze-hide-nth (org-drill-session) 2))
    (should (= 1 overlays-during-prompt))))

(ert-deftest test-multicloze-hide-nth-out-of-range-leaves-no-overlays ()
  "Asking for the 10th piece when only 3 exist hides nothing."
  (with-cloze-card "* Question :drill:
[A] [B] [C]
"
    (with-mocked-prompt-capturing-overlays
     (org-drill-present-multicloze-hide-nth (org-drill-session) 10))
    (should (= 0 overlays-during-prompt))))

(ert-deftest test-multicloze-hide-nth-negative-counts-from-end ()
  "n=-1 means \"the last piece.\""
  (with-cloze-card "* Question :drill:
[A] [B] [C]
"
    (with-mocked-prompt-capturing-overlays
     (org-drill-present-multicloze-hide-nth (org-drill-session) -1))
    (should (= 1 overlays-during-prompt))))

;;;; org-drill--count-cloze-matches (extracted scan helper)

(ert-deftest test-org-drill-count-cloze-matches-counts-body-clozes ()
  "Counts each cloze region between the body bounds."
  (with-cloze-card "* Question :drill:
[A] [B] [C] [D]
"
    (let ((bounds (org-drill--cloze-body-bounds)))
      (should (= 4 (org-drill--count-cloze-matches (car bounds) (cdr bounds)))))))

(ert-deftest test-org-drill-count-cloze-matches-zero-when-no-cloze ()
  "A body with no cloze syntax counts zero."
  (with-cloze-card "* Question :drill:
No cloze syntax here.
"
    (let ((bounds (org-drill--cloze-body-bounds)))
      (should (= 0 (org-drill--count-cloze-matches (car bounds) (cdr bounds)))))))

(provide 'test-org-drill-multicloze-hiding)

;;; test-org-drill-multicloze-hiding.el ends here