aboutsummaryrefslogtreecommitdiff
path: root/tests/test-org-drill-statistics-pass-rate-by-day.el
blob: 38f7ae328295c4a9c959a29b6fcf701f35d5dadb (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
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
;;; test-org-drill-statistics-pass-rate-by-day.el --- Tests for pass-rate-by-day statistics  -*- lexical-binding: t; -*-

;;; Commentary:
;; ERT tests for the org-drill statistics dashboard pass-rate-by-day block.

;;; Code:

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

(defun test-org-drill-statistics--fixed-today (day)
  "Install a stub for `org-drill-statistics--today-day' returning DAY.
Returns nothing useful, used for its side effect inside a fixture."
  (advice-add 'org-drill-statistics--today-day :override
              (lambda () day) '((name . test-fixed-today))))

(defun test-org-drill-statistics--clear-today ()
  "Remove the fixed-today stub installed by the helper above."
  (advice-remove 'org-drill-statistics--today-day 'test-fixed-today))

(defun test-org-drill-statistics--record-on-day (day qualities)
  "Build a session record started on absolute DAY with QUALITIES vector.
DAY is an absolute day number as from `time-to-days'.  QUALITIES is a
vector of integer qualities.  The start-time is the float-time at noon
of that day, so day bucketing is unambiguous."
  (let ((start (+ (float-time
                   (encode-time 0 0 0 1 1 2000))
                  (* (- day (time-to-days
                             (encode-time 0 0 0 1 1 2000)))
                     86400)
                  (* 12 3600))))
    (make-org-drill-session-record
     :start-time start
     :end-time start
     :scope 'directory
     :algorithm 'sm5
     :qualities qualities
     :pass-percent 0
     :new-count 0
     :mature-count 0
     :failed-count 0
     :cram-mode nil)))

;; Normal: a multi-day log produces per-day pass rates in chronological
;; order, with the failure-quality threshold deciding pass vs fail.
(ert-deftest test-org-drill-statistics-pass-rate-by-day-basic ()
  (let ((org-drill-failure-quality 2)
        (today 700000))
    (unwind-protect
        (progn
          (test-org-drill-statistics--fixed-today today)
          ;; today: qualities 5 5 1 0 -> 2 pass of 4 -> 50
          ;; yesterday: qualities 4 3 -> 2 pass of 2 -> 100
          (let* ((log (list
                       (test-org-drill-statistics--record-on-day
                        today [5 5 1 0])
                       (test-org-drill-statistics--record-on-day
                        (1- today) [4 3])))
                 (v (org-drill-statistics--pass-rate-by-day log 3)))
            (should (= (length v) 3))
            (should (null (aref v 0)))          ; two days ago, no data
            (should (= (aref v 1) 100))         ; yesterday
            (should (= (aref v 2) 50))))        ; today
      (test-org-drill-statistics--clear-today))))

;; Normal: multiple records on the same day aggregate together.
(ert-deftest test-org-drill-statistics-pass-rate-by-day-same-day-merge ()
  (let ((org-drill-failure-quality 2)
        (today 700000))
    (unwind-protect
        (progn
          (test-org-drill-statistics--fixed-today today)
          ;; today across two records: [5 1] and [4 0 2]
          ;; passes: 5,4 -> 2 ; total 5 -> 40
          (let* ((log (list
                       (test-org-drill-statistics--record-on-day
                        today [5 1])
                       (test-org-drill-statistics--record-on-day
                        today [4 0 2])))
                 (v (org-drill-statistics--pass-rate-by-day log 1)))
            (should (= (length v) 1))
            (should (= (aref v 0) 40))))
      (test-org-drill-statistics--clear-today))))

;; Boundary: empty log yields an all-nil vector of the requested length.
(ert-deftest test-org-drill-statistics-pass-rate-by-day-empty-log ()
  (let ((today 700000))
    (unwind-protect
        (progn
          (test-org-drill-statistics--fixed-today today)
          (let ((v (org-drill-statistics--pass-rate-by-day nil 5)))
            (should (= (length v) 5))
            (should (cl-every #'null (append v nil)))))
      (test-org-drill-statistics--clear-today))))

;; Boundary: a record outside the window is ignored; one at the oldest
;; edge of the window is counted at index 0.
(ert-deftest test-org-drill-statistics-pass-rate-by-day-window-edges ()
  (let ((org-drill-failure-quality 2)
        (today 700000))
    (unwind-protect
        (progn
          (test-org-drill-statistics--fixed-today today)
          (let* ((days 3)
                 (oldest (- today (1- days)))      ; today-2
                 (log (list
                       ;; just outside the window (too old): ignored
                       (test-org-drill-statistics--record-on-day
                        (1- oldest) [5 5])
                       ;; oldest day in the window: index 0
                       (test-org-drill-statistics--record-on-day
                        oldest [5 0])))
                 (v (org-drill-statistics--pass-rate-by-day log days)))
            (should (= (length v) days))
            (should (= (aref v 0) 50))           ; oldest in-window day
            (should (null (aref v 1)))
            (should (null (aref v 2)))))
      (test-org-drill-statistics--clear-today))))

;; Boundary: a record dated in the future relative to today is ignored.
(ert-deftest test-org-drill-statistics-pass-rate-by-day-future-ignored ()
  (let ((org-drill-failure-quality 2)
        (today 700000))
    (unwind-protect
        (progn
          (test-org-drill-statistics--fixed-today today)
          (let* ((log (list
                       (test-org-drill-statistics--record-on-day
                        (1+ today) [5 5 5])))
                 (v (org-drill-statistics--pass-rate-by-day log 3)))
            (should (cl-every #'null (append v nil)))))
      (test-org-drill-statistics--clear-today))))

;; Boundary: threshold edge. A quality equal to the threshold is a fail;
;; one above it is a pass.
(ert-deftest test-org-drill-statistics-pass-rate-by-day-threshold-edge ()
  (let ((org-drill-failure-quality 2)
        (today 700000))
    (unwind-protect
        (progn
          (test-org-drill-statistics--fixed-today today)
          ;; qualities 2 (fail) and 3 (pass) -> 1 of 2 -> 50
          (let* ((log (list
                       (test-org-drill-statistics--record-on-day
                        today [2 3])))
                 (v (org-drill-statistics--pass-rate-by-day log 1)))
            (should (= (aref v 0) 50))))
      (test-org-drill-statistics--clear-today))))

;; Boundary: a record whose qualities vector is empty contributes no
;; total, leaving that day as no-data rather than a division by zero.
(ert-deftest test-org-drill-statistics-pass-rate-by-day-empty-qualities ()
  (let ((org-drill-failure-quality 2)
        (today 700000))
    (unwind-protect
        (progn
          (test-org-drill-statistics--fixed-today today)
          (let* ((log (list
                       (test-org-drill-statistics--record-on-day
                        today [])))
                 (v (org-drill-statistics--pass-rate-by-day log 1)))
            (should (null (aref v 0)))))
      (test-org-drill-statistics--clear-today))))

;; Boundary: DAYS defaults to `org-drill-statistics-trend-days' when
;; omitted, and a non-positive DAYS is clamped to a length-1 vector.
(ert-deftest test-org-drill-statistics-pass-rate-by-day-days-arg ()
  (let ((org-drill-statistics-trend-days 12)
        (today 700000))
    (unwind-protect
        (progn
          (test-org-drill-statistics--fixed-today today)
          (should (= (length
                      (org-drill-statistics--pass-rate-by-day nil))
                     12))
          (should (= (length
                      (org-drill-statistics--pass-rate-by-day nil 0))
                     1)))
      (test-org-drill-statistics--clear-today))))

;; Error: a nil qualities slot is tolerated as no-data, not a crash.
(ert-deftest test-org-drill-statistics-pass-rate-by-day-nil-qualities ()
  (let ((org-drill-failure-quality 2)
        (today 700000))
    (unwind-protect
        (progn
          (test-org-drill-statistics--fixed-today today)
          (let* ((rec (test-org-drill-statistics--record-on-day
                       today [5 5]))
                 (_ (setf (org-drill-session-record-qualities rec) nil))
                 (v (org-drill-statistics--pass-rate-by-day
                     (list rec) 1)))
            (should (null (aref v 0)))))
      (test-org-drill-statistics--clear-today))))

(provide 'test-org-drill-statistics-pass-rate-by-day)

;;; test-org-drill-statistics-pass-rate-by-day.el ends here