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
|
;;; test-wttrin--resolve-favorite-location.el --- Tests for wttrin--resolve-favorite-location -*- lexical-binding: t; -*-
;; Copyright (C) 2026 Craig Jennings
;;; Commentary:
;; Unit tests for `wttrin--resolve-favorite-location' and its helpers.
;; The resolver maps the three modes of `wttrin-favorite-location'
;; (nil / string / t) onto a returned location string, kicking off an
;; async geolocation lookup the first time t is seen.
;;; Code:
(require 'ert)
(require 'cl-lib)
(require 'wttrin)
(require 'testutil-wttrin)
;; Load wttrin-geolocation up front so cl-letf mocks of
;; `wttrin-geolocation-detect' aren't undone by the resolver's lazy
;; (require 'wttrin-geolocation) — which would otherwise re-evaluate
;; the defun and overwrite the mocked symbol-function.
(require 'wttrin-geolocation
(expand-file-name "wttrin-geolocation.el"
(file-name-directory (locate-library "wttrin"))))
;;; Helpers
(defmacro test-wttrin--resolve-with-geolocation-mock (returned-location &rest body)
"Run BODY with `wttrin-geolocation-detect' mocked to call back with
RETURNED-LOCATION synchronously. Resets resolver caches before BODY."
(declare (indent 1))
`(let ((wttrin--resolved-favorite-location nil)
(wttrin--favorite-location-pending nil))
(cl-letf (((symbol-function 'wttrin-geolocation-detect)
(lambda (callback) (funcall callback ,returned-location))))
,@body)))
;;; Normal Cases
(ert-deftest test-wttrin--resolve-favorite-location-normal-nil-returns-nil ()
"Disabled mode (nil) resolves to nil."
(let ((wttrin-favorite-location nil)
(wttrin--resolved-favorite-location nil)
(wttrin--favorite-location-pending nil))
(should-not (wttrin--resolve-favorite-location))))
(ert-deftest test-wttrin--resolve-favorite-location-normal-string-returns-itself ()
"Explicit string resolves to itself."
(let ((wttrin-favorite-location "Berkeley, CA")
(wttrin--resolved-favorite-location nil)
(wttrin--favorite-location-pending nil))
(should (string= "Berkeley, CA" (wttrin--resolve-favorite-location)))))
(ert-deftest test-wttrin--resolve-favorite-location-normal-t-with-cache-returns-cached ()
"When t and cache populated, resolver returns cached value without re-fetching."
(let ((wttrin-favorite-location t)
(wttrin--resolved-favorite-location "Cached, Place")
(wttrin--favorite-location-pending nil)
(detect-calls 0))
(cl-letf (((symbol-function 'wttrin-geolocation-detect)
(lambda (_callback) (cl-incf detect-calls))))
(should (string= "Cached, Place" (wttrin--resolve-favorite-location)))
(should (= 0 detect-calls)))))
(ert-deftest test-wttrin--resolve-favorite-location-normal-t-detection-success-caches-result ()
"t-mode detect callback populates the resolver cache."
(let ((wttrin-favorite-location t))
(test-wttrin--resolve-with-geolocation-mock "Detected, City"
(wttrin--resolve-favorite-location)
(should (string= "Detected, City" wttrin--resolved-favorite-location))
(should-not wttrin--favorite-location-pending))))
(ert-deftest test-wttrin--resolve-favorite-location-normal-second-call-returns-cached-from-first ()
"After detection completes, the second call returns the cached string."
(let ((wttrin-favorite-location t))
(test-wttrin--resolve-with-geolocation-mock "Detected, City"
(wttrin--resolve-favorite-location)
(should (string= "Detected, City" (wttrin--resolve-favorite-location))))))
;;; Boundary Cases
(ert-deftest test-wttrin--resolve-favorite-location-boundary-t-without-cache-returns-nil ()
"First call with t and empty cache returns nil while detection runs."
(let ((wttrin-favorite-location t)
(wttrin--resolved-favorite-location nil)
(wttrin--favorite-location-pending nil))
(cl-letf (((symbol-function 'wttrin-geolocation-detect)
(lambda (_callback)
;; Simulate an async lookup that has NOT called back yet.
(setq wttrin--favorite-location-pending t))))
(should-not (wttrin--resolve-favorite-location)))))
(ert-deftest test-wttrin--resolve-favorite-location-boundary-pending-suppresses-duplicate-detect ()
"When a lookup is already pending, the resolver does not start another."
(let ((wttrin-favorite-location t)
(wttrin--resolved-favorite-location nil)
(wttrin--favorite-location-pending t)
(detect-calls 0))
(cl-letf (((symbol-function 'wttrin-geolocation-detect)
(lambda (_callback) (cl-incf detect-calls))))
(wttrin--resolve-favorite-location)
(should (= 0 detect-calls)))))
;;; Error Cases
(ert-deftest test-wttrin--resolve-favorite-location-error-detection-failure-leaves-cache-empty ()
"When the detect callback returns nil, the cache stays empty and pending clears."
(let ((wttrin-favorite-location t))
(test-wttrin--resolve-with-geolocation-mock nil
(wttrin--resolve-favorite-location)
(should-not wttrin--resolved-favorite-location)
(should-not wttrin--favorite-location-pending))))
(ert-deftest test-wttrin--resolve-favorite-location-error-detection-failure-allows-retry ()
"After a failed detection, a subsequent resolve call kicks off a new lookup."
(let ((wttrin-favorite-location t)
(wttrin--resolved-favorite-location nil)
(wttrin--favorite-location-pending nil)
(detect-calls 0))
(cl-letf (((symbol-function 'wttrin-geolocation-detect)
(lambda (callback)
(cl-incf detect-calls)
(funcall callback nil))))
(wttrin--resolve-favorite-location)
(wttrin--resolve-favorite-location)
(should (= 2 detect-calls)))))
;;; Display Name Helper
(ert-deftest test-wttrin--favorite-location-display-name-resolved-string-returns-string ()
"Display name returns the resolved string when available."
(let ((wttrin-favorite-location "Tokyo, JP")
(wttrin--resolved-favorite-location nil)
(wttrin--favorite-location-pending nil))
(should (string= "Tokyo, JP" (wttrin--favorite-location-display-name)))))
(ert-deftest test-wttrin--favorite-location-display-name-t-pending-returns-current-location-label ()
"Display name returns \"current location\" when t-mode lookup is pending."
(let ((wttrin-favorite-location t)
(wttrin--resolved-favorite-location nil)
(wttrin--favorite-location-pending t))
(should (string= "current location"
(wttrin--favorite-location-display-name)))))
(ert-deftest test-wttrin--favorite-location-display-name-nil-returns-nil ()
"Display name returns nil when feature is disabled."
(let ((wttrin-favorite-location nil)
(wttrin--resolved-favorite-location nil)
(wttrin--favorite-location-pending nil))
(should-not (wttrin--favorite-location-display-name))))
(provide 'test-wttrin--resolve-favorite-location)
;;; test-wttrin--resolve-favorite-location.el ends here
|