aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-04-26 18:29:27 -0500
committerCraig Jennings <c@cjennings.net>2026-04-26 18:29:27 -0500
commiteabd510c302b43b0b40ee7b92bf5830d345d058d (patch)
tree31a73830e61e14850343a024558610088343bbd1
parentfe653a231b186393b02a2723985e61ce97baba1c (diff)
downloademacs-wttrin-eabd510c302b43b0b40ee7b92bf5830d345d058d.tar.gz
emacs-wttrin-eabd510c302b43b0b40ee7b92bf5830d345d058d.zip
refactor: extract wttrin--mode-line-stale-p helper
The staleness check `(> age (* 2 wttrin-mode-line-refresh-interval))` lived in two places, `wttrin--mode-line-tooltip` and `wttrin--mode-line-update-display`, along with the four-line preamble that read the timestamp out of the cache cons and computed age. Centralizing the rule in a single helper means the threshold lives in one spot. That makes it easy to add a `wttrin-mode-line-staleness-threshold` defcustom later if the magic 2× ever needs to be tunable. The helper takes a cache entry (or nil) and returns t/nil. Five new tests cover Normal (fresh, stale) and Boundary (just below the threshold, just past, nil entry). The boundary tests use 199s and 201s against a 100s refresh interval to lock the strict `>` semantics with comfortable float-time margins. In `wttrin--mode-line-update-display` the refactor also drops two locals (`timestamp` and `age`) that were no longer used after the helper call replaced the inline calculation. Behavior is unchanged at both call sites.
-rw-r--r--tests/test-wttrin--mode-line-helpers.el36
-rw-r--r--wttrin.el18
2 files changed, 49 insertions, 5 deletions
diff --git a/tests/test-wttrin--mode-line-helpers.el b/tests/test-wttrin--mode-line-helpers.el
index 34523a8..6eae822 100644
--- a/tests/test-wttrin--mode-line-helpers.el
+++ b/tests/test-wttrin--mode-line-helpers.el
@@ -135,5 +135,41 @@ distinguish a missing key from a present key bound to nil."
'mode-line-highlight)))
(test-wttrin--mode-line-helpers-teardown)))
+;;; --------------------------------------------------------------------------
+;;; wttrin--mode-line-stale-p
+;;; --------------------------------------------------------------------------
+
+;;; Normal Cases
+
+(ert-deftest test-wttrin--mode-line-stale-p-normal-fresh-cache-returns-nil ()
+ "Cache entry well within the refresh window is not stale."
+ (let ((wttrin-mode-line-refresh-interval 100))
+ (let ((entry (cons (- (float-time) 50) "data")))
+ (should-not (wttrin--mode-line-stale-p entry)))))
+
+(ert-deftest test-wttrin--mode-line-stale-p-normal-stale-cache-returns-t ()
+ "Cache entry older than 2× refresh-interval is stale."
+ (let ((wttrin-mode-line-refresh-interval 100))
+ (let ((entry (cons (- (float-time) 500) "data")))
+ (should (wttrin--mode-line-stale-p entry)))))
+
+;;; Boundary Cases
+
+(ert-deftest test-wttrin--mode-line-stale-p-boundary-just-below-threshold ()
+ "Age 1s below the 2× threshold is not stale (strict >)."
+ (let ((wttrin-mode-line-refresh-interval 100))
+ (let ((entry (cons (- (float-time) 199) "data")))
+ (should-not (wttrin--mode-line-stale-p entry)))))
+
+(ert-deftest test-wttrin--mode-line-stale-p-boundary-just-past-threshold ()
+ "Age 1s past the 2× threshold is stale."
+ (let ((wttrin-mode-line-refresh-interval 100))
+ (let ((entry (cons (- (float-time) 201) "data")))
+ (should (wttrin--mode-line-stale-p entry)))))
+
+(ert-deftest test-wttrin--mode-line-stale-p-boundary-nil-cache-returns-nil ()
+ "Nil cache-entry returns nil with no signal."
+ (should-not (wttrin--mode-line-stale-p nil)))
+
(provide 'test-wttrin--mode-line-helpers)
;;; test-wttrin--mode-line-helpers.el ends here
diff --git a/wttrin.el b/wttrin.el
index 9036a5f..eb66868 100644
--- a/wttrin.el
+++ b/wttrin.el
@@ -676,6 +676,16 @@ On failure with no cache, shows error placeholder."
;; No cache at all — show error placeholder
(wttrin--mode-line-update-placeholder-error))))))))
+(defun wttrin--mode-line-stale-p (cache-entry)
+ "Return non-nil if CACHE-ENTRY is stale.
+Stale means age greater than 2 × `wttrin-mode-line-refresh-interval'.
+CACHE-ENTRY is a (TIMESTAMP . WEATHER-STRING) cons or nil. A nil
+entry returns nil so callers can pass `wttrin--mode-line-cache' directly
+without a separate guard."
+ (when cache-entry
+ (let ((age (- (float-time) (car cache-entry))))
+ (> age (* 2 wttrin-mode-line-refresh-interval)))))
+
(defun wttrin--mode-line-tooltip (&optional _window _object _pos)
"Compute tooltip text from `wttrin--mode-line-cache'.
Calculates age at call time so the tooltip is always current.
@@ -686,7 +696,7 @@ Optional arguments are ignored (required by `help-echo' function protocol)."
(let* ((timestamp (car wttrin--mode-line-cache))
(weather-string (cdr wttrin--mode-line-cache))
(age (- (float-time) timestamp))
- (stale-p (> age (* 2 wttrin-mode-line-refresh-interval)))
+ (stale-p (wttrin--mode-line-stale-p wttrin--mode-line-cache))
(age-str (wttrin--format-age age)))
;; Re-render emoji if staleness state has changed
(unless (eq stale-p wttrin--mode-line-rendered-stale)
@@ -702,10 +712,8 @@ Reads cached weather data, computes age, and sets the mode-line string.
If data is stale (age > 2x refresh interval), dims the emoji and
shows staleness info in tooltip."
(when wttrin--mode-line-cache
- (let* ((timestamp (car wttrin--mode-line-cache))
- (weather-string (cdr wttrin--mode-line-cache))
- (age (- (float-time) timestamp))
- (stale-p (> age (* 2 wttrin-mode-line-refresh-interval))))
+ (let* ((weather-string (cdr wttrin--mode-line-cache))
+ (stale-p (wttrin--mode-line-stale-p wttrin--mode-line-cache)))
(wttrin--debug-log "mode-line-display: Updating from cache, stale=%s" stale-p)
;; Response format is "Location: ☀️ +72°F Clear" — grab first char after colon
(let ((emoji (if (string-match ":\\s-*\\(.\\)" weather-string)