blob: b346bf7768172437c72a22090b3d838a08d499d1 (
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
|
;;; test-calendar-sync--syncing-p.el --- Tests for the in-flight sync guard -*- lexical-binding: t; -*-
;;; Commentary:
;; Unit tests for `calendar-sync--syncing-p' (the per-calendar in-flight check
;; that lets the dispatcher skip an overlapping timer tick) and for the
;; load-state sanitize that clears a stale `syncing' status in a fresh process.
;;; Code:
(require 'ert)
(require 'calendar-sync)
(defun test-cs-syncing--reset ()
"Clear the module's per-calendar state hash."
(clrhash calendar-sync--calendar-states))
;;; calendar-sync--syncing-p
(ert-deftest test-calendar-sync--syncing-p-normal-true-when-syncing ()
"Normal: a calendar whose status is `syncing' reads as in-flight."
(test-cs-syncing--reset)
(calendar-sync--set-calendar-state "google" '(:status syncing))
(should (calendar-sync--syncing-p "google")))
(ert-deftest test-calendar-sync--syncing-p-boundary-nil-when-no-state ()
"Boundary: a calendar with no recorded state is not in-flight."
(test-cs-syncing--reset)
(should-not (calendar-sync--syncing-p "never-seen")))
(ert-deftest test-calendar-sync--syncing-p-error-nil-for-terminal-status ()
"Error: a terminal status (ok / error) is not in-flight."
(test-cs-syncing--reset)
(calendar-sync--set-calendar-state "google" '(:status ok))
(should-not (calendar-sync--syncing-p "google"))
(calendar-sync--set-calendar-state "proton" '(:status error))
(should-not (calendar-sync--syncing-p "proton")))
;;; Dispatcher guard: an in-flight calendar skips both leaf syncers
(ert-deftest test-calendar-sync--sync-calendar-skips-when-in-flight ()
"Normal: `calendar-sync--sync-calendar' does not launch a second sync for a
calendar already marked syncing, so an overlapping timer tick is a no-op."
(test-cs-syncing--reset)
(let ((api-calls '()) (ics-calls '()))
(cl-letf (((symbol-function 'calendar-sync--sync-calendar-api)
(lambda (cal) (push cal api-calls)))
((symbol-function 'calendar-sync--sync-calendar-ics)
(lambda (cal) (push cal ics-calls))))
(calendar-sync--set-calendar-state "proton" '(:status syncing))
(calendar-sync--sync-calendar '(:name "proton" :url "https://x/y.ics"
:file "/tmp/c.org"))
(should (null api-calls))
(should (null ics-calls)))))
(ert-deftest test-calendar-sync--sync-calendar-dispatches-when-idle ()
"Boundary: an idle calendar (no in-flight status) still dispatches normally."
(test-cs-syncing--reset)
(let ((ics-calls '()))
(cl-letf (((symbol-function 'calendar-sync--sync-calendar-ics)
(lambda (cal) (push cal ics-calls))))
(calendar-sync--sync-calendar '(:name "proton" :url "https://x/y.ics"
:file "/tmp/c.org"))
(should (= 1 (length ics-calls))))))
;;; load-state sanitize: a persisted `syncing' status is cleared on load
(ert-deftest test-calendar-sync--load-state-clears-stale-syncing ()
"Error: a `syncing' status persisted before a crash is reset on load, so the
in-flight guard cannot skip that calendar forever in the new session."
(test-cs-syncing--reset)
(let* ((dir (make-temp-file "cs-state-" t))
(calendar-sync--state-file (expand-file-name "state.el" dir)))
(unwind-protect
(progn
(with-temp-file calendar-sync--state-file
(prin1 '((timezone-offset . nil)
(calendar-states . (("google" . (:status syncing)))))
(current-buffer)))
(calendar-sync--load-state)
(should-not (calendar-sync--syncing-p "google")))
(delete-directory dir t))))
(provide 'test-calendar-sync--syncing-p)
;;; test-calendar-sync--syncing-p.el ends here
|