aboutsummaryrefslogtreecommitdiff
path: root/tests/test-chime-tooltip-day-calculation.el
diff options
context:
space:
mode:
Diffstat (limited to 'tests/test-chime-tooltip-day-calculation.el')
-rw-r--r--tests/test-chime-tooltip-day-calculation.el326
1 files changed, 326 insertions, 0 deletions
diff --git a/tests/test-chime-tooltip-day-calculation.el b/tests/test-chime-tooltip-day-calculation.el
new file mode 100644
index 0000000..5d0901d
--- /dev/null
+++ b/tests/test-chime-tooltip-day-calculation.el
@@ -0,0 +1,326 @@
+;;; test-chime-tooltip-day-calculation.el --- Tests for tooltip day/hour calculation -*- lexical-binding: t; -*-
+
+;;; Commentary:
+;; Comprehensive tests for tooltip time-until formatting, especially day/hour calculations.
+;;
+;; Tests cover:
+;; - Boundary cases (23h59m, 24h, 25h)
+;; - Midnight boundaries
+;; - Multiple days with fractional hours
+;; - Exact day boundaries (48h, 72h)
+;; - Edge cases that could trigger truncation bugs
+
+;;; Code:
+
+(require 'ert)
+(require 'package)
+(setq package-user-dir (expand-file-name "~/.emacs.d/elpa"))
+(package-initialize)
+(load (expand-file-name "../chime.el") nil t)
+(require 'testutil-time (expand-file-name "testutil-time.el"))
+(require 'testutil-general (expand-file-name "testutil-general.el"))
+
+(ert-deftest test-chime-tooltip-day-calculation-fractional-days ()
+ "Test that fractional days show both days and hours correctly.
+
+User scenario: Viewing tooltip on Sunday 9pm, sees:
+- Tuesday 9pm event: 48 hours = exactly 2 days → 'in 2 days'
+- Wednesday 2pm event: 65 hours = 2.7 days → 'in 2 days 17 hours'
+
+This test prevents regression of the integer division truncation bug."
+ (chime-create-test-base-dir)
+ (unwind-protect
+ (let* ((now (test-time-today-at 21 0)) ; Sunday 9pm
+ ;; Create events at specific future times
+ (tuesday-9pm (time-add now (seconds-to-time (* 48 3600)))) ; +48 hours
+ (wednesday-2pm (time-add now (seconds-to-time (* 65 3600)))) ; +65 hours
+ (content (format "* Tuesday Event\n<%s>\n* Wednesday Event\n<%s>\n"
+ (format-time-string "<%Y-%m-%d %a %H:%M>" tuesday-9pm)
+ (format-time-string "<%Y-%m-%d %a %H:%M>" wednesday-2pm)))
+ (test-file (chime-create-temp-test-file-with-content content))
+ (test-buffer (find-file-noselect test-file))
+ (events nil))
+
+ ;; Gather events
+ (with-current-buffer test-buffer
+ (org-mode)
+ (goto-char (point-min))
+ (while (re-search-forward "^\\*+ " nil t)
+ (beginning-of-line)
+ (push (chime--gather-info (point-marker)) events)
+ (forward-line 1)))
+ (kill-buffer test-buffer)
+ (setq events (nreverse events))
+
+ ;; Set lookahead to cover events (7 days)
+ (setq chime-modeline-lookahead-minutes 10080)
+ (setq chime-tooltip-lookahead-hours 168)
+
+ (with-test-time now
+ ;; Update modeline and get tooltip
+ (chime--update-modeline events)
+ (let ((tooltip (chime--make-tooltip chime--upcoming-events)))
+
+ ;; Verify tooltip contains both events
+ (should (string-match-p "Tuesday Event" tooltip))
+ (should (string-match-p "Wednesday Event" tooltip))
+
+ ;; Print tooltip for manual inspection
+ (message "TOOLTIP CONTENT:\n%s" tooltip)
+
+ ;; AFTER FIX: Tuesday shows "in 2 days", Wednesday shows "in 2 days 17 hours"
+ ;; Verify Tuesday shows exactly 2 days (no "hours" in countdown)
+ (should (string-match-p "Tuesday Event.*(in 2 days)" tooltip))
+ ;; Make sure Tuesday doesn't have hours
+ (should-not (string-match-p "Tuesday Event.*hours" tooltip))
+
+ ;; Verify Wednesday shows 2 days AND 17 hours
+ (should (string-match-p "Wednesday Event.*(in 2 days 17 hours)" tooltip))
+
+ ;; Verify they show DIFFERENT countdowns
+ (let ((tuesday-line (progn
+ (string-match "Tuesday Event[^\n]*" tooltip)
+ (match-string 0 tooltip)))
+ (wednesday-line (progn
+ (string-match "Wednesday Event[^\n]*" tooltip)
+ (match-string 0 tooltip))))
+ (should-not (string= tuesday-line wednesday-line))))))
+
+ (chime-delete-test-base-dir)))
+
+;;; Helper function for creating test events
+
+(defun test-chime-tooltip-day-calculation--create-event-at-hours (base-time title hours-from-now)
+ "Create event with TITLE at HOURS-FROM-NOW hours from BASE-TIME.
+Returns formatted org content string."
+ (let* ((event-time (time-add base-time (seconds-to-time (* hours-from-now 3600)))))
+ (format "* %s\n<%s>\n"
+ title
+ (format-time-string "%Y-%m-%d %a %H:%M" event-time))))
+
+(defun test-chime-tooltip-day-calculation--get-formatted-line (tooltip event-name)
+ "Extract the formatted countdown line for EVENT-NAME from TOOLTIP."
+ (when (string-match (format "%s[^\n]*" event-name) tooltip)
+ (match-string 0 tooltip)))
+
+;;; Boundary Cases - Critical thresholds
+
+(ert-deftest test-chime-tooltip-day-calculation-boundary-exactly-24-hours ()
+ "Test event exactly 24 hours away shows 'in 1 day' not hours."
+ (chime-create-test-base-dir)
+ (unwind-protect
+ (let* ((now (test-time-today-at 12 0))
+ (content (test-chime-tooltip-day-calculation--create-event-at-hours now "Tomorrow Same Time" 24))
+ (test-file (chime-create-temp-test-file-with-content content))
+ (events (with-current-buffer (find-file-noselect test-file)
+ (org-mode)
+ (goto-char (point-min))
+ (let ((evs nil))
+ (while (re-search-forward "^\\*+ " nil t)
+ (push (chime--gather-info (point-marker)) evs))
+ (nreverse evs)))))
+ (kill-buffer (get-file-buffer test-file))
+
+ (setq chime-modeline-lookahead-minutes 10080)
+ (setq chime-tooltip-lookahead-hours 168)
+
+ (with-test-time now
+ (chime--update-modeline events)
+ (let ((tooltip (chime--make-tooltip chime--upcoming-events)))
+ ;; Should show "in 1 day" not hours
+ (should (string-match-p "(in 1 day)" tooltip))
+ (should-not (string-match-p "hours" tooltip)))))
+ (chime-delete-test-base-dir)))
+
+(ert-deftest test-chime-tooltip-day-calculation-boundary-23-hours-59-minutes ()
+ "Test event 23h59m away shows hours, not days (just under 24h threshold)."
+ (chime-create-test-base-dir)
+ (unwind-protect
+ (let* ((now (test-time-today-at 12 0))
+ ;; 23 hours 59 minutes = 1439 minutes = just under 1440
+ (event-time (time-add now (seconds-to-time (* 1439 60))))
+ (content (format "* Almost Tomorrow\n<%s>\n"
+ (format-time-string "%Y-%m-%d %a %H:%M" event-time)))
+ (test-file (chime-create-temp-test-file-with-content content))
+ (events (with-current-buffer (find-file-noselect test-file)
+ (org-mode)
+ (goto-char (point-min))
+ (let ((evs nil))
+ (while (re-search-forward "^\\*+ " nil t)
+ (push (chime--gather-info (point-marker)) evs))
+ (nreverse evs)))))
+ (kill-buffer (get-file-buffer test-file))
+
+ (setq chime-modeline-lookahead-minutes 10080)
+ (setq chime-tooltip-lookahead-hours 168)
+
+ (with-test-time now
+ (chime--update-modeline events)
+ (let ((tooltip (chime--make-tooltip chime--upcoming-events)))
+ ;; Should show hours format (< 24 hours)
+ (should (string-match-p "hours" tooltip))
+ (should-not (string-match-p "days?" tooltip)))))
+ (chime-delete-test-base-dir)))
+
+(ert-deftest test-chime-tooltip-day-calculation-boundary-25-hours ()
+ "Test event 25 hours away shows 'in 1 day 1 hour'."
+ (chime-create-test-base-dir)
+ (unwind-protect
+ (let* ((now (test-time-today-at 12 0))
+ (content (test-chime-tooltip-day-calculation--create-event-at-hours now "Day Plus One" 25))
+ (test-file (chime-create-temp-test-file-with-content content))
+ (events (with-current-buffer (find-file-noselect test-file)
+ (org-mode)
+ (goto-char (point-min))
+ (let ((evs nil))
+ (while (re-search-forward "^\\*+ " nil t)
+ (push (chime--gather-info (point-marker)) evs))
+ (nreverse evs)))))
+ (kill-buffer (get-file-buffer test-file))
+
+ (setq chime-modeline-lookahead-minutes 10080)
+ (setq chime-tooltip-lookahead-hours 168)
+
+ (with-test-time now
+ (chime--update-modeline events)
+ (let ((tooltip (chime--make-tooltip chime--upcoming-events)))
+ ;; Should show "in 1 day 1 hour"
+ (should (string-match-p "(in 1 day 1 hour)" tooltip)))))
+ (chime-delete-test-base-dir)))
+
+(ert-deftest test-chime-tooltip-day-calculation-boundary-exactly-48-hours ()
+ "Test event exactly 48 hours away shows 'in 2 days' without hours."
+ (chime-create-test-base-dir)
+ (unwind-protect
+ (let* ((now (test-time-today-at 12 0))
+ (content (test-chime-tooltip-day-calculation--create-event-at-hours now "Two Days Exact" 48))
+ (test-file (chime-create-temp-test-file-with-content content))
+ (events (with-current-buffer (find-file-noselect test-file)
+ (org-mode)
+ (goto-char (point-min))
+ (let ((evs nil))
+ (while (re-search-forward "^\\*+ " nil t)
+ (push (chime--gather-info (point-marker)) evs))
+ (nreverse evs)))))
+ (kill-buffer (get-file-buffer test-file))
+
+ (setq chime-modeline-lookahead-minutes 10080)
+ (setq chime-tooltip-lookahead-hours 168)
+
+ (with-test-time now
+ (chime--update-modeline events)
+ (let ((tooltip (chime--make-tooltip chime--upcoming-events))
+ (line (test-chime-tooltip-day-calculation--get-formatted-line
+ (chime--make-tooltip chime--upcoming-events) "Two Days Exact")))
+ ;; Should show exactly "in 2 days" with NO hours
+ (should (string-match-p "(in 2 days)" tooltip))
+ ;; Verify the line doesn't contain "hour" (would be "2 days 0 hours")
+ (should-not (string-match-p "hour" line)))))
+ (chime-delete-test-base-dir)))
+
+;;; Midnight Boundaries
+
+(ert-deftest test-chime-tooltip-day-calculation-midnight-crossing-shows-correct-days ()
+ "Test event crossing midnight boundary calculates days correctly.
+
+Scenario: 11pm now, event at 2am (3 hours later, next calendar day)
+Should show hours, not '1 day' since it's only 3 hours away."
+ (chime-create-test-base-dir)
+ (unwind-protect
+ (let* ((now (test-time-today-at 23 0)) ; 11pm
+ ;; 3 hours later = 2am next day
+ (content (test-chime-tooltip-day-calculation--create-event-at-hours now "Early Morning" 3))
+ (test-file (chime-create-temp-test-file-with-content content))
+ (events (with-current-buffer (find-file-noselect test-file)
+ (org-mode)
+ (goto-char (point-min))
+ (let ((evs nil))
+ (while (re-search-forward "^\\*+ " nil t)
+ (push (chime--gather-info (point-marker)) evs))
+ (nreverse evs)))))
+ (kill-buffer (get-file-buffer test-file))
+
+ (setq chime-modeline-lookahead-minutes 10080)
+ (setq chime-tooltip-lookahead-hours 168)
+
+ (with-test-time now
+ (chime--update-modeline events)
+ (let ((tooltip (chime--make-tooltip chime--upcoming-events)))
+ ;; Should show "in 3 hours" not "in 1 day"
+ (should (string-match-p "3 hours" tooltip))
+ (should-not (string-match-p "days?" tooltip)))))
+ (chime-delete-test-base-dir)))
+
+(ert-deftest test-chime-tooltip-day-calculation-midnight-plus-one-day ()
+ "Test event at midnight tomorrow (24h exactly) shows '1 day'."
+ (chime-create-test-base-dir)
+ (unwind-protect
+ (let* ((now (test-time-today-at 0 0)) ; Midnight today
+ (content (test-chime-tooltip-day-calculation--create-event-at-hours now "Midnight Tomorrow" 24))
+ (test-file (chime-create-temp-test-file-with-content content))
+ (events (with-current-buffer (find-file-noselect test-file)
+ (org-mode)
+ (goto-char (point-min))
+ (let ((evs nil))
+ (while (re-search-forward "^\\*+ " nil t)
+ (push (chime--gather-info (point-marker)) evs))
+ (nreverse evs)))))
+ (kill-buffer (get-file-buffer test-file))
+
+ (setq chime-modeline-lookahead-minutes 10080)
+ (setq chime-tooltip-lookahead-hours 168)
+
+ (with-test-time now
+ (chime--update-modeline events)
+ (let ((tooltip (chime--make-tooltip chime--upcoming-events)))
+ (should (string-match-p "(in 1 day)" tooltip))
+ (should-not (string-match-p "hour" tooltip)))))
+ (chime-delete-test-base-dir)))
+
+;;; Multiple Events - Verify distinct formatting
+
+(ert-deftest test-chime-tooltip-day-calculation-multiple-events-distinct ()
+ "Test multiple events at different fractional-day offsets show distinct times."
+ (chime-create-test-base-dir)
+ (unwind-protect
+ (let* ((now (test-time-today-at 12 0))
+ (content (concat
+ (test-chime-tooltip-day-calculation--create-event-at-hours now "Event 1 Day" 24)
+ (test-chime-tooltip-day-calculation--create-event-at-hours now "Event 1.5 Days" 36)
+ (test-chime-tooltip-day-calculation--create-event-at-hours now "Event 2 Days" 48)
+ (test-chime-tooltip-day-calculation--create-event-at-hours now "Event 2.75 Days" 66)))
+ (test-file (chime-create-temp-test-file-with-content content))
+ (events (with-current-buffer (find-file-noselect test-file)
+ (org-mode)
+ (goto-char (point-min))
+ (let ((evs nil))
+ (while (re-search-forward "^\\*+ " nil t)
+ (push (chime--gather-info (point-marker)) evs))
+ (nreverse evs)))))
+ (kill-buffer (get-file-buffer test-file))
+
+ (setq chime-modeline-lookahead-minutes 10080)
+ (setq chime-tooltip-lookahead-hours 168)
+
+ (with-test-time now
+ (chime--update-modeline events)
+ (let ((tooltip (chime--make-tooltip chime--upcoming-events)))
+ ;; Verify each event shows correctly
+ (should (string-match-p "Event 1 Day.*(in 1 day)" tooltip))
+ (should (string-match-p "Event 1.5 Days.*(in 1 day 12 hours)" tooltip))
+ (should (string-match-p "Event 2 Days.*(in 2 days)" tooltip))
+ (should (string-match-p "Event 2.75 Days.*(in 2 days 18 hours)" tooltip))
+
+ ;; Verify they're all different
+ (let ((lines (split-string tooltip "\n")))
+ (let ((countdowns (cl-remove-if-not
+ (lambda (line) (string-match-p "Event.*day" line))
+ lines)))
+ ;; Should have 4 distinct countdown lines
+ (should (= 4 (length countdowns)))
+ ;; All should be unique
+ (should (= 4 (length (delete-dups (copy-sequence countdowns))))))))))
+ (chime-delete-test-base-dir)))
+
+(provide 'test-chime-tooltip-day-calculation)
+;;; test-chime-tooltip-day-calculation.el ends here