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
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
|
;;; test-chime-modeline-no-events-text.el --- Tests for chime-modeline-no-events-text customization -*- lexical-binding: t; -*-
;; Copyright (C) 2024-2026 Craig Jennings
;; Author: Craig Jennings <c@cjennings.net>
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; Unit tests for chime-modeline-no-events-text defcustom.
;; Tests the modeline display when no events are within lookahead window.
;;
;; Tests three scenarios:
;; 1. Setting is nil → show nothing in modeline
;; 2. Setting is custom text → show that text
;; 3. Event within lookahead → show event (ignores setting)
;;; Code:
;; Initialize package system for batch mode
(when noninteractive
(package-initialize))
(require 'ert)
;; Load dependencies required by chime
(require 'dash)
(require 'alert)
(require 'async)
(require 'org-agenda)
;; Load chime from parent directory
(load (expand-file-name "../chime.el") nil t)
;; Load test utilities
(require 'testutil-general (expand-file-name "testutil-general.el"))
(require 'testutil-time (expand-file-name "testutil-time.el"))
;;; Setup and Teardown
(defvar test-chime-modeline-no-events-text--orig-lookahead nil)
(defvar test-chime-modeline-no-events-text--orig-tooltip-lookahead nil)
(defvar test-chime-modeline-no-events-text--orig-no-events-text nil)
(defun test-chime-modeline-no-events-text-setup ()
"Setup function run before each test."
(chime-create-test-base-dir)
;; Save original values
(setq test-chime-modeline-no-events-text--orig-lookahead chime-modeline-lookahead-minutes)
(setq test-chime-modeline-no-events-text--orig-tooltip-lookahead chime-tooltip-lookahead-hours)
(setq test-chime-modeline-no-events-text--orig-no-events-text chime-modeline-no-events-text)
;; Set short lookahead for testing
(setq chime-modeline-lookahead-minutes 60) ; 1 hour
(setq chime-tooltip-lookahead-hours 24)) ; 24 hours
(defun test-chime-modeline-no-events-text-teardown ()
"Teardown function run after each test."
;; Restore original values
(setq chime-modeline-lookahead-minutes test-chime-modeline-no-events-text--orig-lookahead)
(setq chime-tooltip-lookahead-hours test-chime-modeline-no-events-text--orig-tooltip-lookahead)
(setq chime-modeline-no-events-text test-chime-modeline-no-events-text--orig-no-events-text)
(chime-delete-test-base-dir))
;;; Helper Functions
(defun test-chime-modeline-no-events-text--create-event (title time-offset-hours)
"Create org content for event with TITLE at TIME-OFFSET-HOURS from now."
(let* ((event-time (test-time-at 0 time-offset-hours 0))
(timestamp (test-timestamp-string event-time)))
(format "* TODO %s\nSCHEDULED: %s\n" title timestamp)))
(defun test-chime-modeline-no-events-text--gather-events (content)
"Process CONTENT like chime-check does and return events list."
(let* ((test-file (chime-create-temp-test-file-with-content content))
(test-buffer (find-file-noselect test-file))
(events nil))
(with-current-buffer test-buffer
(org-mode)
(goto-char (point-min))
(while (re-search-forward "^\\*+ " nil t)
(beginning-of-line)
(let* ((marker (point-marker))
(info (chime--gather-info marker)))
(push info events))
(forward-line 1)))
(kill-buffer test-buffer)
(nreverse events)))
(defun test-chime-modeline-no-events-text--update-and-get-modeline (events-content)
"Create org file with EVENTS-CONTENT, update modeline, return chime-modeline-string."
(let ((events (test-chime-modeline-no-events-text--gather-events events-content)))
(chime--update-modeline events)
;; Return the modeline string
chime-modeline-string))
;;; Normal Cases
(ert-deftest test-chime-modeline-no-events-text-normal-nil-setting-no-events-in-lookahead-returns-nil ()
"Test that nil setting shows nothing when events exist beyond lookahead.
When chime-modeline-no-events-text is nil and events exist beyond
the lookahead window but not within it, the modeline should be nil
(show nothing)."
(test-chime-modeline-no-events-text-setup)
(unwind-protect
(let* ((now (test-time-now))
;; Event 3 hours from now (beyond 1-hour lookahead)
(content (test-chime-modeline-no-events-text--create-event "Future Event" 3)))
;; Set to nil (default)
(setq chime-modeline-no-events-text nil)
(with-test-time now
(let ((result (test-chime-modeline-no-events-text--update-and-get-modeline content)))
(should (null result)))))
(test-chime-modeline-no-events-text-teardown)))
(ert-deftest test-chime-modeline-no-events-text-normal-custom-text-no-events-in-lookahead-returns-text ()
"Test that custom text displays when events exist beyond lookahead.
When chime-modeline-no-events-text is \" 🔕\" and events exist beyond
the lookahead window, the modeline should show \" 🔕\"."
(test-chime-modeline-no-events-text-setup)
(unwind-protect
(let* ((now (test-time-now))
;; Event 3 hours from now (beyond 1-hour lookahead)
(content (test-chime-modeline-no-events-text--create-event "Future Event" 3)))
;; Set custom text
(setq chime-modeline-no-events-text " 🔕")
(with-test-time now
(let ((result (test-chime-modeline-no-events-text--update-and-get-modeline content)))
(should (stringp result))
(should (string-match-p "🔕" result)))))
(test-chime-modeline-no-events-text-teardown)))
(ert-deftest test-chime-modeline-no-events-text-normal-event-within-lookahead-shows-event ()
"Test that event within lookahead is shown, ignoring no-events-text.
When an event is within the lookahead window, the modeline should show
the event regardless of chime-modeline-no-events-text setting."
(test-chime-modeline-no-events-text-setup)
(unwind-protect
(let* ((now (test-time-now))
;; Event 30 minutes from now (within 1-hour lookahead)
(content (test-chime-modeline-no-events-text--create-event "Upcoming Event" 0.5)))
;; Set custom text (should be ignored)
(setq chime-modeline-no-events-text " 🔕")
(with-test-time now
(let ((result (test-chime-modeline-no-events-text--update-and-get-modeline content)))
(should (stringp result))
(should (string-match-p "Upcoming Event" result))
(should-not (string-match-p "🔕" result)))))
(test-chime-modeline-no-events-text-teardown)))
(ert-deftest test-chime-modeline-no-events-text-normal-custom-text-has-tooltip ()
"Test that custom text has tooltip when displayed.
When chime-modeline-no-events-text is displayed, it should have a
help-echo property with the tooltip showing upcoming events."
(test-chime-modeline-no-events-text-setup)
(unwind-protect
(let* ((now (test-time-now))
;; Event 3 hours from now (beyond 1-hour lookahead)
(content (test-chime-modeline-no-events-text--create-event "Future Event" 3)))
;; Set custom text
(setq chime-modeline-no-events-text " 🔕")
(with-test-time now
(let ((result (test-chime-modeline-no-events-text--update-and-get-modeline content)))
(should (stringp result))
;; Check for help-echo property (tooltip)
(should (get-text-property 0 'help-echo result)))))
(test-chime-modeline-no-events-text-teardown)))
;;; Boundary Cases
(ert-deftest test-chime-modeline-no-events-text-boundary-empty-string-no-events-returns-empty ()
"Test that empty string setting shows empty string.
When chime-modeline-no-events-text is \"\" (empty string) and events
exist beyond lookahead, the modeline should show empty string."
(test-chime-modeline-no-events-text-setup)
(unwind-protect
(let* ((now (test-time-now))
;; Event 3 hours from now (beyond 1-hour lookahead)
(content (test-chime-modeline-no-events-text--create-event "Future Event" 3)))
;; Set to empty string
(setq chime-modeline-no-events-text "")
(with-test-time now
(let ((result (test-chime-modeline-no-events-text--update-and-get-modeline content)))
(should (stringp result))
(should (equal "" result)))))
(test-chime-modeline-no-events-text-teardown)))
(ert-deftest test-chime-modeline-no-events-text-boundary-no-events-at-all-shows-icon ()
"Test that icon appears even when there are no events at all.
When there are zero events (not just beyond lookahead, but none at all),
the modeline should still show the icon with a helpful tooltip explaining
that there are no events and suggesting to increase the lookahead window."
(test-chime-modeline-no-events-text-setup)
(unwind-protect
(let* ((now (test-time-now))
;; No events - empty content
(content ""))
;; Set custom text
(setq chime-modeline-no-events-text " 🔕")
(with-test-time now
(let ((result (test-chime-modeline-no-events-text--update-and-get-modeline content)))
;; Should show icon (not nil)
(should result)
(should (stringp result))
(should (equal " 🔕" (substring-no-properties result)))
;; Should have tooltip explaining no events
(let ((tooltip (get-text-property 0 'help-echo result)))
(should tooltip)
(should (string-match-p "No calendar events" tooltip))
(should (string-match-p "chime-tooltip-lookahead-hours" tooltip))))))
(test-chime-modeline-no-events-text-teardown)))
(ert-deftest test-chime-modeline-no-events-text-boundary-very-long-text-displays-correctly ()
"Test that very long custom text displays correctly.
When chime-modeline-no-events-text is a very long string (50+ chars),
the modeline should show the full string."
(test-chime-modeline-no-events-text-setup)
(unwind-protect
(let* ((now (test-time-now))
;; Event 3 hours from now (beyond 1-hour lookahead)
(content (test-chime-modeline-no-events-text--create-event "Future Event" 3))
(long-text " No events within the next hour, but some later today"))
;; Set very long text
(setq chime-modeline-no-events-text long-text)
(with-test-time now
(let ((result (test-chime-modeline-no-events-text--update-and-get-modeline content)))
(should (stringp result))
(should (equal long-text result)))))
(test-chime-modeline-no-events-text-teardown)))
(ert-deftest test-chime-modeline-no-events-text-boundary-special-characters-emoji-displays ()
"Test that special characters and emoji display correctly.
When chime-modeline-no-events-text contains emoji and unicode characters,
they should display correctly in the modeline."
(test-chime-modeline-no-events-text-setup)
(unwind-protect
(let* ((now (test-time-now))
;; Event 3 hours from now (beyond 1-hour lookahead)
(content (test-chime-modeline-no-events-text--create-event "Future Event" 3))
(emoji-text " 🔕🔔⏰📅"))
;; Set emoji text
(setq chime-modeline-no-events-text emoji-text)
(with-test-time now
(let ((result (test-chime-modeline-no-events-text--update-and-get-modeline content)))
(should (stringp result))
(should (equal emoji-text result)))))
(test-chime-modeline-no-events-text-teardown)))
;;; Error Cases
(ert-deftest test-chime-modeline-no-events-text-error-whitespace-only-displays-whitespace ()
"Test that whitespace-only setting displays as-is.
When chime-modeline-no-events-text is \" \" (whitespace only),
it should display the whitespace without trimming."
(test-chime-modeline-no-events-text-setup)
(unwind-protect
(let* ((now (test-time-now))
;; Event 3 hours from now (beyond 1-hour lookahead)
(content (test-chime-modeline-no-events-text--create-event "Future Event" 3))
(whitespace-text " "))
;; Set whitespace text
(setq chime-modeline-no-events-text whitespace-text)
(with-test-time now
(let ((result (test-chime-modeline-no-events-text--update-and-get-modeline content)))
(should (stringp result))
(should (equal whitespace-text result)))))
(test-chime-modeline-no-events-text-teardown)))
(provide 'test-chime-modeline-no-events-text)
;;; test-chime-modeline-no-events-text.el ends here
|