aboutsummaryrefslogtreecommitdiff
path: root/tests/test-org-drill-statistics-forecast.el
blob: cf6efae420e33f676c9e652553b1a774ab4b3da8 (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
;;; test-org-drill-statistics-forecast.el --- Tests for forecast statistics  -*- lexical-binding: t; -*-

;;; Commentary:
;; ERT tests for the org-drill statistics dashboard forecast block.

;;; Code:

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

;;; Tests for org-drill-statistics--forecast and its pure bucketer.
;;
;; The bucketing math is exercised directly against
;; `org-drill-statistics--bucket-forecast-days' with synthetic day
;; numbers (no org buffer needed).  The org-reading path is exercised
;; through a with-temp-buffer fixture whose SCHEDULED dates are derived
;; relative to (current-time), never hardcoded.


(defun test-org-drill-statistics--scheduled-offset-string (days-from-today)
  "Return an active org SCHEDULED stamp DAYS-FROM-TODAY days out.
Derived from `current-time' so the fixture never hardcodes a date."
  (format-time-string
   "<%Y-%m-%d %a>"
   (time-add (current-time) (days-to-time days-from-today))))

(defun test-org-drill-statistics--forecast-fixture (offsets)
  "Insert one scheduled drill card per integer in OFFSETS.
Each card is tagged `drill' and scheduled that many days from today.
Returns nothing; call inside a `with-temp-buffer' after `org-mode'."
  (let ((n 0))
    (dolist (off offsets)
      (setq n (1+ n))
      (insert (format "* Card %d :drill:\nSCHEDULED: %s\n"
                      n
                      (test-org-drill-statistics--scheduled-offset-string off))))))

;; Normal cases --------------------------------------------------------

(ert-deftest test-org-drill-statistics-forecast-bucket-counts-by-offset ()
  "Bucketer tallies each day-offset into the matching index."
  (let ((today 1000))
    ;; Two due today, one due tomorrow, one due in 3 days.
    (should (equal (org-drill-statistics--bucket-forecast-days
                    (list 1000 1000 1001 1003) today 7)
                   '(2 1 0 1 0 0 0)))))

(ert-deftest test-org-drill-statistics-forecast-bucket-empty-input ()
  "Bucketer returns an all-zero vector of the requested length."
  (should (equal (org-drill-statistics--bucket-forecast-days nil 500 5)
                 '(0 0 0 0 0))))

(ert-deftest test-org-drill-statistics-forecast-reads-scheduled-entries ()
  "Forecast over a fixture counts cards by their SCHEDULED day."
  (with-temp-buffer
    (org-mode)
    ;; today, today, tomorrow, day-after, plus a far-future card.
    (test-org-drill-statistics--forecast-fixture '(0 0 1 2 30))
    (let ((org-drill-scope 'file)
          (org-drill-question-tag "drill")
          (org-drill-match nil))
      ;; Default window of 7: the day-30 card falls outside it.
      ;; Offsets 0,0,1,2 bucket to today=2, +1=1, +2=1; +30 dropped.
      (should (equal (org-drill-statistics--forecast 'file 7)
                     '(2 1 1 0 0 0 0))))))

;; Boundary cases ------------------------------------------------------

(ert-deftest test-org-drill-statistics-forecast-bucket-window-edges ()
  "Cards on the last in-window day count; the next day does not."
  (let ((today 0))
    ;; offset 6 is the last bucket of a 7-day window; offset 7 is out.
    (should (equal (org-drill-statistics--bucket-forecast-days
                    (list 6 7) today 7)
                   '(0 0 0 0 0 0 1)))))

(ert-deftest test-org-drill-statistics-forecast-bucket-past-and-future-dropped ()
  "Days before today and beyond the window are not counted."
  (let ((today 100))
    (should (equal (org-drill-statistics--bucket-forecast-days
                    (list 99 100 110) today 3)
                   '(1 0 0)))))

(ert-deftest test-org-drill-statistics-forecast-bucket-single-day-window ()
  "A one-day window yields a single bucket counting today's cards."
  ;; Two cards due today, one due tomorrow; a window of 1 keeps only the
  ;; today bucket, so the tomorrow card is dropped.
  (should (equal (org-drill-statistics--bucket-forecast-days
                  (list 50 50 51) 50 1)
                 '(2))))

(ert-deftest test-org-drill-statistics-forecast-skips-unscheduled-entries ()
  "Entries without a SCHEDULED time are ignored by the reader."
  (with-temp-buffer
    (org-mode)
    (insert "* Scheduled card :drill:\nSCHEDULED: "
            (test-org-drill-statistics--scheduled-offset-string 0)
            "\n* No-schedule card :drill:\n")
    (let ((org-drill-scope 'file)
          (org-drill-question-tag "drill")
          (org-drill-match nil))
      (should (equal (org-drill-statistics--scheduled-days 'file)
                     (list (org-drill-statistics--today-day))))
      (should (equal (org-drill-statistics--forecast 'file 3)
                     '(1 0 0))))))

;; Error / degenerate cases -------------------------------------------

(ert-deftest test-org-drill-statistics-forecast-bucket-zero-days ()
  "A zero-length window yields the empty list."
  (should (equal (org-drill-statistics--bucket-forecast-days
                  (list 10 11) 10 0)
                 nil)))

(ert-deftest test-org-drill-statistics-forecast-bucket-negative-days ()
  "A negative window length is treated as empty, not an error."
  (should (equal (org-drill-statistics--bucket-forecast-days
                  (list 10 11) 10 -3)
                 nil)))

(ert-deftest test-org-drill-statistics-forecast-empty-scope ()
  "A scope with no drill entries forecasts all zeros."
  (with-temp-buffer
    (org-mode)
    (insert "* Not a card\nSome prose.\n")
    (let ((org-drill-scope 'file)
          (org-drill-question-tag "drill")
          (org-drill-match nil))
      (should (equal (org-drill-statistics--forecast 'file 4)
                     '(0 0 0 0))))))

(provide 'test-org-drill-statistics-forecast)

;;; test-org-drill-statistics-forecast.el ends here