aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--modules/calendar-sync.el32
-rw-r--r--tests/test-calendar-sync.el45
2 files changed, 67 insertions, 10 deletions
diff --git a/modules/calendar-sync.el b/modules/calendar-sync.el
index 804d71fa..d504f246 100644
--- a/modules/calendar-sync.el
+++ b/modules/calendar-sync.el
@@ -300,16 +300,28 @@ When called non-interactively with nil, syncs all calendars."
;;; Timer management
(defun calendar-sync--sync-timer-function ()
- "Function called by sync timer.
-Checks for timezone changes and triggers re-sync if detected."
- (when (calendar-sync--timezone-changed-p)
- (let ((old-tz (calendar-sync--format-timezone-offset
- calendar-sync--last-timezone-offset))
- (new-tz (calendar-sync--format-timezone-offset
- (calendar-sync--current-timezone-offset))))
- (message "calendar-sync: Timezone change detected (%s → %s), re-syncing..."
- old-tz new-tz)))
- (calendar-sync--sync-all-calendars))
+ "Function called by the hourly sync timer.
+Checks for timezone changes and triggers re-sync if detected.
+
+The body is wrapped so a signal — from the timezone check or the sync
+fan-out — is caught and logged rather than propagated: this runs from a
+`run-at-time' timer, and an unguarded error would repeat on every tick,
+once an hour, indefinitely. The timezone-change notice goes to the silent
+log, not the echo area, since an hourly timer must not spam `message'."
+ (condition-case err
+ (progn
+ (when (calendar-sync--timezone-changed-p)
+ (let ((old-tz (calendar-sync--format-timezone-offset
+ calendar-sync--last-timezone-offset))
+ (new-tz (calendar-sync--format-timezone-offset
+ (calendar-sync--current-timezone-offset))))
+ (calendar-sync--log-silently
+ "calendar-sync: Timezone change detected (%s → %s), re-syncing..."
+ old-tz new-tz)))
+ (calendar-sync--sync-all-calendars))
+ (error
+ (calendar-sync--log-silently
+ "calendar-sync: sync timer error: %s" (error-message-string err)))))
;;;###autoload
(defun calendar-sync-start ()
diff --git a/tests/test-calendar-sync.el b/tests/test-calendar-sync.el
index f562cfc6..8a7c2549 100644
--- a/tests/test-calendar-sync.el
+++ b/tests/test-calendar-sync.el
@@ -713,5 +713,50 @@ Valid events should be parsed, invalid ones skipped."
(should-not (and org-content
(string-match-p "OutOfRangeEvent" org-content)))))
+;;; calendar-sync--sync-timer-function — hourly-timer body hygiene
+
+(ert-deftest test-calendar-sync-timer-function-does-not-propagate-a-signal ()
+ "Error: a signal in the timer body is caught, not propagated.
+The function runs from an hourly `run-at-time' timer. An unguarded signal
+in the timezone check or the sync fan-out would error on every tick — the
+same error, once an hour, forever. It must swallow-and-log instead."
+ (cl-letf (((symbol-function 'calendar-sync--timezone-changed-p)
+ (lambda (&rest _) (error "boom from the timezone check")))
+ ((symbol-function 'calendar-sync--sync-all-calendars) #'ignore)
+ ((symbol-function 'calendar-sync--log-silently) #'ignore))
+ ;; Must return normally rather than signal.
+ (should (progn (calendar-sync--sync-timer-function) t))))
+
+(ert-deftest test-calendar-sync-timer-function-signal-in-sync-is-caught ()
+ "Error: a signal from the sync fan-out is also caught, not propagated."
+ (cl-letf (((symbol-function 'calendar-sync--timezone-changed-p) #'ignore)
+ ((symbol-function 'calendar-sync--sync-all-calendars)
+ (lambda (&rest _) (error "boom from sync-all")))
+ ((symbol-function 'calendar-sync--log-silently) #'ignore))
+ (should (progn (calendar-sync--sync-timer-function) t))))
+
+(ert-deftest test-calendar-sync-timer-function-timezone-change-is-not-echoed ()
+ "Normal: a detected timezone change is logged silently, not echoed.
+An hourly timer that calls `message' spams the echo area; the notice belongs
+in the silent log like the module's other timer-path notices."
+ (let (silent-logged echoed)
+ (cl-letf (((symbol-function 'calendar-sync--timezone-changed-p)
+ (lambda (&rest _) t))
+ ((symbol-function 'calendar-sync--format-timezone-offset)
+ (lambda (&rest _) "UTC+0"))
+ ((symbol-function 'calendar-sync--current-timezone-offset)
+ (lambda (&rest _) 0))
+ ((symbol-function 'calendar-sync--sync-all-calendars) #'ignore)
+ ((symbol-function 'calendar-sync--log-silently)
+ (lambda (fmt &rest _) (when (string-match-p "Timezone" fmt)
+ (setq silent-logged t))))
+ ((symbol-function 'message)
+ (lambda (fmt &rest _) (when (and (stringp fmt)
+ (string-match-p "Timezone" fmt))
+ (setq echoed t)))))
+ (calendar-sync--sync-timer-function)
+ (should silent-logged)
+ (should-not echoed))))
+
(provide 'test-calendar-sync)
;;; test-calendar-sync.el ends here