From eabd510c302b43b0b40ee7b92bf5830d345d058d Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Sun, 26 Apr 2026 18:29:27 -0500 Subject: refactor: extract wttrin--mode-line-stale-p helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- tests/test-wttrin--mode-line-helpers.el | 36 +++++++++++++++++++++++++++++++++ wttrin.el | 18 ++++++++++++----- 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) -- cgit v1.2.3