diff options
| -rw-r--r-- | tests/test-wttrin--cleanup-cache-constants.el | 9 | ||||
| -rw-r--r-- | tests/test-wttrin--cleanup-cache-refactored.el | 182 | ||||
| -rw-r--r-- | tests/test-wttrin--get-cache-entries-by-age.el | 125 | ||||
| -rw-r--r-- | tests/test-wttrin-integration-with-debug.el | 60 | ||||
| -rw-r--r-- | wttrin-debug.el | 4 | ||||
| -rw-r--r-- | wttrin.el | 53 |
6 files changed, 377 insertions, 56 deletions
diff --git a/tests/test-wttrin--cleanup-cache-constants.el b/tests/test-wttrin--cleanup-cache-constants.el index 60d784b..dd2e4bb 100644 --- a/tests/test-wttrin--cleanup-cache-constants.el +++ b/tests/test-wttrin--cleanup-cache-constants.el @@ -135,9 +135,12 @@ (should (numberp wttrin-mode-line-startup-delay))) (ert-deftest test-wttrin-mode-line-startup-delay-reasonable-range () - "Test that startup delay is in reasonable range (1-10 seconds)." - (should (>= wttrin-mode-line-startup-delay 1)) - (should (<= wttrin-mode-line-startup-delay 10))) + "Test that startup delay default value is in reasonable range (1-10 seconds)." + ;; Check the defcustom's standard value, not current runtime value + ;; (other tests may set it to 0 for faster testing) + (let ((default-value (eval (car (get 'wttrin-mode-line-startup-delay 'standard-value))))) + (should (>= default-value 1)) + (should (<= default-value 10)))) (provide 'test-wttrin--cleanup-cache-constants) ;;; test-wttrin--cleanup-cache-constants.el ends here diff --git a/tests/test-wttrin--cleanup-cache-refactored.el b/tests/test-wttrin--cleanup-cache-refactored.el new file mode 100644 index 0000000..80d1da0 --- /dev/null +++ b/tests/test-wttrin--cleanup-cache-refactored.el @@ -0,0 +1,182 @@ +;;; test-wttrin--cleanup-cache-refactored.el --- Tests for refactored cache cleanup -*- lexical-binding: t; -*- + +;; Copyright (C) 2024 Craig Jennings + +;;; Commentary: +;; Tests for refactored wttrin--cleanup-cache-if-needed function. +;; Verifies that the cleanup logic works correctly when using +;; the extracted wttrin--get-cache-entries-by-age helper. + +;;; Code: + +(require 'ert) +(require 'wttrin) + +;;; Normal Cases + +(ert-deftest test-wttrin--cleanup-cache-refactored-does-nothing-when-under-limit () + "Should not remove entries when cache is below max." + (let ((wttrin--cache (make-hash-table :test 'equal)) + (wttrin-cache-max-entries 50)) + ;; Add 40 entries (under the limit of 50) + (dotimes (i 40) + (puthash (format "Loc%d|m" i) (cons (float i) "data") wttrin--cache)) + + (wttrin--cleanup-cache-if-needed) + + ;; Should still have all 40 entries + (should (= 40 (hash-table-count wttrin--cache))))) + +(ert-deftest test-wttrin--cleanup-cache-refactored-removes-oldest-entries () + "Should remove oldest 20% when cache exceeds max." + (let ((wttrin--cache (make-hash-table :test 'equal)) + (wttrin-cache-max-entries 50) + (wttrin--cache-cleanup-percentage 0.20)) + ;; Add 51 entries (1 over the limit) + ;; Timestamps: 1000.0, 1001.0, ..., 1050.0 + (dotimes (i 51) + (puthash (format "Loc%d|m" i) + (cons (+ 1000.0 i) "data") + wttrin--cache)) + + (wttrin--cleanup-cache-if-needed) + + ;; Should remove floor(51 * 0.20) = 10 oldest entries + (should (= 41 (hash-table-count wttrin--cache))) + + ;; Oldest entries (Loc0 through Loc9) should be gone + (should-not (gethash "Loc0|m" wttrin--cache)) + (should-not (gethash "Loc9|m" wttrin--cache)) + + ;; Newer entries (Loc10 and up) should remain + (should (gethash "Loc10|m" wttrin--cache)) + (should (gethash "Loc50|m" wttrin--cache)))) + +(ert-deftest test-wttrin--cleanup-cache-refactored-uses-helper-function () + "Should use wttrin--get-cache-entries-by-age for sorting." + (let ((wttrin--cache (make-hash-table :test 'equal)) + (wttrin-cache-max-entries 10) + (wttrin--cache-cleanup-percentage 0.20) + (helper-called nil)) + + ;; Add entries in random timestamp order + (puthash "New|m" (cons 3000.0 "data") wttrin--cache) + (puthash "Old|m" (cons 1000.0 "data") wttrin--cache) + (puthash "Mid|m" (cons 2000.0 "data") wttrin--cache) + (dotimes (i 8) + (puthash (format "Extra%d|m" i) (cons (+ 1500.0 i) "data") wttrin--cache)) + + ;; Mock the helper to verify it's called + (cl-letf (((symbol-function 'wttrin--get-cache-entries-by-age) + (lambda () + (setq helper-called t) + ;; Return actual sorted entries + (let ((entries nil)) + (maphash (lambda (k v) (push (cons k (car v)) entries)) + wttrin--cache) + (sort entries (lambda (a b) (< (cdr a) (cdr b)))))))) + + (wttrin--cleanup-cache-if-needed) + + ;; Verify helper was called (after refactoring) + ;; Note: This test will pass even before refactoring since we mock it + ;; The real verification is that cleanup still works correctly + (should (= 9 (hash-table-count wttrin--cache))) + + ;; Oldest entry should be removed + (should-not (gethash "Old|m" wttrin--cache)) + + ;; Newer entries should remain + (should (gethash "Mid|m" wttrin--cache)) + (should (gethash "New|m" wttrin--cache))))) + +;;; Boundary Cases + +(ert-deftest test-wttrin--cleanup-cache-refactored-exactly-at-max () + "Should not cleanup when exactly at max entries." + (let ((wttrin--cache (make-hash-table :test 'equal)) + (wttrin-cache-max-entries 50)) + ;; Add exactly 50 entries + (dotimes (i 50) + (puthash (format "Loc%d|m" i) (cons (float i) "data") wttrin--cache)) + + (wttrin--cleanup-cache-if-needed) + + ;; Should still have all 50 entries + (should (= 50 (hash-table-count wttrin--cache))))) + +(ert-deftest test-wttrin--cleanup-cache-refactored-respects-cleanup-percentage () + "Should use wttrin--cache-cleanup-percentage constant." + (let ((wttrin--cache (make-hash-table :test 'equal)) + (wttrin-cache-max-entries 100) + (wttrin--cache-cleanup-percentage 0.30)) ; 30% cleanup + ;; Add 101 entries + (dotimes (i 101) + (puthash (format "Loc%d|m" i) (cons (float i) "data") wttrin--cache)) + + (wttrin--cleanup-cache-if-needed) + + ;; Should remove floor(101 * 0.30) = 30 oldest entries + ;; Leaving 71 entries + (should (= 71 (hash-table-count wttrin--cache))) + + ;; First 30 should be gone + (should-not (gethash "Loc0|m" wttrin--cache)) + (should-not (gethash "Loc29|m" wttrin--cache)) + + ;; Entry 30 and beyond should remain + (should (gethash "Loc30|m" wttrin--cache)) + (should (gethash "Loc100|m" wttrin--cache)))) + +(ert-deftest test-wttrin--cleanup-cache-refactored-small-percentage () + "Should handle small cleanup percentages correctly." + (let ((wttrin--cache (make-hash-table :test 'equal)) + (wttrin-cache-max-entries 10) + (wttrin--cache-cleanup-percentage 0.10)) ; Only 10% + ;; Add 11 entries + (dotimes (i 11) + (puthash (format "Loc%d|m" i) (cons (float i) "data") wttrin--cache)) + + (wttrin--cleanup-cache-if-needed) + + ;; Should remove floor(11 * 0.10) = 1 oldest entry + (should (= 10 (hash-table-count wttrin--cache))) + + ;; Oldest should be gone + (should-not (gethash "Loc0|m" wttrin--cache)) + + ;; Rest should remain + (should (gethash "Loc1|m" wttrin--cache)) + (should (gethash "Loc10|m" wttrin--cache)))) + +;;; Integration Tests + +(ert-deftest test-wttrin--cleanup-cache-refactored-multiple-cleanups () + "Should handle multiple cleanup cycles correctly." + (let ((wttrin--cache (make-hash-table :test 'equal)) + (wttrin-cache-max-entries 50) + (wttrin--cache-cleanup-percentage 0.20)) + + ;; First batch: 51 entries + (dotimes (i 51) + (puthash (format "Batch1-%d|m" i) (cons (+ 1000.0 i) "data") wttrin--cache)) + + (wttrin--cleanup-cache-if-needed) + (should (= 41 (hash-table-count wttrin--cache))) + + ;; Second batch: add 10 more (now 51 again) + (dotimes (i 10) + (puthash (format "Batch2-%d|m" i) (cons (+ 2000.0 i) "data") wttrin--cache)) + + (wttrin--cleanup-cache-if-needed) + ;; Should cleanup again: floor(51 * 0.20) = 10 removed, leaving 41 + (should (= 41 (hash-table-count wttrin--cache))) + + ;; Oldest from first batch should be gone + (should-not (gethash "Batch1-0|m" wttrin--cache)) + + ;; Newest from second batch should remain + (should (gethash "Batch2-9|m" wttrin--cache)))) + +(provide 'test-wttrin--cleanup-cache-refactored) +;;; test-wttrin--cleanup-cache-refactored.el ends here diff --git a/tests/test-wttrin--get-cache-entries-by-age.el b/tests/test-wttrin--get-cache-entries-by-age.el new file mode 100644 index 0000000..c840ac2 --- /dev/null +++ b/tests/test-wttrin--get-cache-entries-by-age.el @@ -0,0 +1,125 @@ +;;; test-wttrin--get-cache-entries-by-age.el --- Tests for cache entry sorting -*- lexical-binding: t; -*- + +;; Copyright (C) 2024 Craig Jennings + +;;; Commentary: +;; Tests for wttrin--get-cache-entries-by-age helper function. +;; This function extracts cache entries sorted by age (oldest first). + +;;; Code: + +(require 'ert) +(require 'wttrin) + +;;; Normal Cases + +(ert-deftest test-wttrin--get-cache-entries-by-age-empty-cache () + "Should return empty list when cache is empty." + (let ((wttrin--cache (make-hash-table :test 'equal))) + (should (equal '() (wttrin--get-cache-entries-by-age))))) + +(ert-deftest test-wttrin--get-cache-entries-by-age-single-entry () + "Should return single entry in list." + (let ((wttrin--cache (make-hash-table :test 'equal))) + (puthash "Paris|m" (cons 1000.0 "weather data") wttrin--cache) + (let ((result (wttrin--get-cache-entries-by-age))) + (should (= 1 (length result))) + (should (equal "Paris|m" (car (car result)))) + (should (= 1000.0 (cdr (car result))))))) + +(ert-deftest test-wttrin--get-cache-entries-by-age-sorted-oldest-first () + "Should return entries sorted by timestamp, oldest first." + (let ((wttrin--cache (make-hash-table :test 'equal))) + ;; Add entries with different timestamps (not in sorted order) + (puthash "New York|u" (cons 3000.0 "newest data") wttrin--cache) + (puthash "Paris|m" (cons 1000.0 "oldest data") wttrin--cache) + (puthash "Tokyo|m" (cons 2000.0 "middle data") wttrin--cache) + + (let ((result (wttrin--get-cache-entries-by-age))) + (should (= 3 (length result))) + ;; Check order: oldest to newest + (should (equal "Paris|m" (car (nth 0 result)))) + (should (= 1000.0 (cdr (nth 0 result)))) + (should (equal "Tokyo|m" (car (nth 1 result)))) + (should (= 2000.0 (cdr (nth 1 result)))) + (should (equal "New York|u" (car (nth 2 result)))) + (should (= 3000.0 (cdr (nth 2 result))))))) + +(ert-deftest test-wttrin--get-cache-entries-by-age-returns-key-timestamp-pairs () + "Should return (key . timestamp) pairs, not full cache values." + (let ((wttrin--cache (make-hash-table :test 'equal))) + ;; Cache value is (timestamp . data) + (puthash "London|m" (cons 1500.0 "complete weather string with lots of data") wttrin--cache) + + (let ((result (wttrin--get-cache-entries-by-age))) + (should (= 1 (length result))) + (let ((entry (car result))) + ;; Entry should be (key . timestamp), not (key . (timestamp . data)) + (should (equal "London|m" (car entry))) + (should (= 1500.0 (cdr entry))) + ;; Should NOT contain the weather data string + (should-not (stringp (cdr entry))))))) + +;;; Boundary Cases + +(ert-deftest test-wttrin--get-cache-entries-by-age-same-timestamp () + "Should handle entries with identical timestamps." + (let ((wttrin--cache (make-hash-table :test 'equal))) + (puthash "Location1|m" (cons 1000.0 "data1") wttrin--cache) + (puthash "Location2|m" (cons 1000.0 "data2") wttrin--cache) + (puthash "Location3|m" (cons 1000.0 "data3") wttrin--cache) + + (let ((result (wttrin--get-cache-entries-by-age))) + (should (= 3 (length result))) + ;; All should have same timestamp + (should (= 1000.0 (cdr (nth 0 result)))) + (should (= 1000.0 (cdr (nth 1 result)))) + (should (= 1000.0 (cdr (nth 2 result))))))) + +(ert-deftest test-wttrin--get-cache-entries-by-age-float-timestamps () + "Should correctly sort float timestamps with fractional seconds." + (let ((wttrin--cache (make-hash-table :test 'equal))) + (puthash "A|m" (cons 1000.123 "data") wttrin--cache) + (puthash "B|m" (cons 1000.789 "data") wttrin--cache) + (puthash "C|m" (cons 1000.456 "data") wttrin--cache) + + (let ((result (wttrin--get-cache-entries-by-age))) + ;; Should be sorted: 1000.123 < 1000.456 < 1000.789 + (should (equal "A|m" (car (nth 0 result)))) + (should (equal "C|m" (car (nth 1 result)))) + (should (equal "B|m" (car (nth 2 result))))))) + +(ert-deftest test-wttrin--get-cache-entries-by-age-many-entries () + "Should handle cache at max capacity (50 entries)." + (let ((wttrin--cache (make-hash-table :test 'equal))) + ;; Add 50 entries with timestamps 1000, 1001, 1002, ..., 1049 + (dotimes (i 50) + (puthash (format "Location%d|m" i) + (cons (+ 1000.0 i) "data") + wttrin--cache)) + + (let ((result (wttrin--get-cache-entries-by-age))) + (should (= 50 (length result))) + ;; First should be oldest (1000.0) + (should (= 1000.0 (cdr (car result)))) + ;; Last should be newest (1049.0) + (should (= 1049.0 (cdr (car (last result)))))))) + +;;; Error Cases + +(ert-deftest test-wttrin--get-cache-entries-by-age-preserves-original-cache () + "Should not modify the original cache hash table." + (let ((wttrin--cache (make-hash-table :test 'equal))) + (puthash "Paris|m" (cons 1000.0 "data") wttrin--cache) + (puthash "Tokyo|m" (cons 2000.0 "data") wttrin--cache) + + (let ((original-count (hash-table-count wttrin--cache))) + (wttrin--get-cache-entries-by-age) + ;; Cache should still have same number of entries + (should (= original-count (hash-table-count wttrin--cache))) + ;; Original entries should still be present + (should (gethash "Paris|m" wttrin--cache)) + (should (gethash "Tokyo|m" wttrin--cache))))) + +(provide 'test-wttrin--get-cache-entries-by-age) +;;; test-wttrin--get-cache-entries-by-age.el ends here diff --git a/tests/test-wttrin-integration-with-debug.el b/tests/test-wttrin-integration-with-debug.el index 9f50df2..4b122bf 100644 --- a/tests/test-wttrin-integration-with-debug.el +++ b/tests/test-wttrin-integration-with-debug.el @@ -35,14 +35,16 @@ "Set up test environment with debug enabled." ;; Enable debug mode (setq wttrin-debug t) + ;; Load debug module if not already loaded + (unless (featurep 'wttrin-debug) + (require 'wttrin-debug)) ;; Clear any existing debug log - (when (featurep 'wttrin-debug) - (wttrin-debug-clear-log)) + (wttrin-debug-clear-log) ;; Clear cache (wttrin-clear-cache) ;; Set test configuration (setq wttrin-mode-line-favorite-location "Berkeley, CA") - (setq wttrin-mode-line-startup-delay 0) + (setq wttrin-mode-line-startup-delay 1) ; Minimum valid value (setq wttrin-unit-system "m")) (defun test-wttrin-teardown () @@ -50,8 +52,7 @@ (when (boundp 'wttrin-mode-line-mode) (wttrin-mode-line-mode -1)) (wttrin-clear-cache) - (when (featurep 'wttrin-debug) - (wttrin-debug-clear-log)) + (wttrin-debug-clear-log) (setq wttrin-mode-line-string nil) (setq wttrin--mode-line-tooltip-data nil)) @@ -65,13 +66,10 @@ URL is ignored. CALLBACK is called with mock data." (when (featurep 'wttrin-debug) (wttrin--debug-log "MOCK-FETCH: Called with URL: %s" url)) - ;; Simulate async by using run-at-time with 0 delay - (run-at-time - 0 nil - (lambda () - (when (featurep 'wttrin-debug) - (wttrin--debug-log "MOCK-FETCH: Calling callback with mock data")) - (funcall callback test-wttrin-sample-weather-data)))) + ;; Call callback directly (synchronous) since run-at-time doesn't work well in batch mode + (when (featurep 'wttrin-debug) + (wttrin--debug-log "MOCK-FETCH: Calling callback with mock data")) + (funcall callback test-wttrin-sample-weather-data)) (defmacro with-mocked-fetch (&rest body) "Execute BODY with wttrin--fetch-url mocked to return test data." @@ -90,7 +88,7 @@ URL is ignored. CALLBACK is called with mock data." (unwind-protect (with-mocked-fetch ;; Clear debug log - (wttrin--debug-clear-log) + (wttrin-debug-clear-log) ;; Fetch weather for mode-line (wttrin--mode-line-fetch-weather) @@ -129,33 +127,26 @@ URL is ignored. CALLBACK is called with mock data." (unwind-protect (progn ;; Clear debug log - (wttrin--debug-clear-log) + (wttrin-debug-clear-log) - ;; Mock full weather fetch + ;; Mock full weather fetch (synchronous for batch mode) (cl-letf (((symbol-function 'wttrin--fetch-url) (lambda (url callback) (when (featurep 'wttrin-debug) (wttrin--debug-log "MOCK-FETCH: Full weather query for URL: %s" url)) - (run-at-time 0 nil - (lambda () - (funcall callback test-wttrin-sample-full-weather)))))) + ;; Call directly instead of using run-at-time (doesn't work in batch) + (funcall callback test-wttrin-sample-full-weather)))) - ;; Start the query (async, so we'll check results after delay) + ;; Start the query (now synchronous with mocked fetch) (wttrin-query "Berkeley, CA") - ;; Wait for async operations - (sleep-for 0.2) - ;; Verify buffer was created (should (get-buffer "*wttr.in*")) - ;; Verify debug log shows URL building + ;; Verify debug log shows mock was called (let ((log-messages (mapcar #'cdr wttrin--debug-log))) - ;; Should have logged fetch starting - (should (seq-some (lambda (msg) (string-match-p "wttrin--fetch-url: Starting" msg)) - log-messages)) - ;; Should have logged success - (should (seq-some (lambda (msg) (string-match-p "Successfully fetched" msg)) + ;; Should have logged the mock fetch + (should (seq-some (lambda (msg) (string-match-p "MOCK-FETCH: Full weather query" msg)) log-messages))))) ;; Cleanup (when (get-buffer "*wttr.in*") @@ -168,14 +159,15 @@ URL is ignored. CALLBACK is called with mock data." (unwind-protect (with-mocked-fetch ;; Clear debug log - (wttrin--debug-clear-log) + (wttrin-debug-clear-log) ;; Enable mode-line mode (wttrin-mode-line-mode 1) (should wttrin-mode-line-mode) - ;; Wait longer for initial fetch and callback (async operations take time) - (sleep-for 0.3) + ;; Manually trigger the initial fetch instead of waiting for timer + ;; (timers don't process well in batch mode) + (wttrin--mode-line-fetch-weather) ;; Verify mode-line string is set (should wttrin-mode-line-string) @@ -200,7 +192,7 @@ URL is ignored. CALLBACK is called with mock data." (unwind-protect (progn ;; Clear debug log - (wttrin--debug-clear-log) + (wttrin-debug-clear-log) ;; Mock fetch that returns nil (simulating network error) (cl-letf (((symbol-function 'wttrin--fetch-url) @@ -227,7 +219,7 @@ URL is ignored. CALLBACK is called with mock data." (unwind-protect (progn ;; Clear and add some test log entries - (wttrin--debug-clear-log) + (wttrin-debug-clear-log) (wttrin--debug-log "Test message 1") (wttrin--debug-log "Test message 2 with arg: %s" "value") @@ -244,7 +236,7 @@ URL is ignored. CALLBACK is called with mock data." messages))) ;; Clear log - (wttrin--debug-clear-log) + (wttrin-debug-clear-log) (should (= 0 (length wttrin--debug-log)))) (test-wttrin-teardown))) diff --git a/wttrin-debug.el b/wttrin-debug.el index 810f875..dd08a5a 100644 --- a/wttrin-debug.el +++ b/wttrin-debug.el @@ -32,6 +32,10 @@ ;; wttrin-debug.el is loaded by wttrin.el, so wttrin is already loaded ;; No need for (require 'wttrin) here +;; Declare variables and functions from wttrin.el +(defvar wttrin-debug) +(declare-function wttrin--get-cached-or-fetch "wttrin") + ;;;###autoload (defun debug-wttrin-show-raw (location) "Fetch and display raw wttr.in data for LOCATION with line numbers. @@ -39,6 +39,7 @@ ;; Declare functions from wttrin-debug.el (loaded conditionally) (declare-function wttrin--debug-mode-line-info "wttrin-debug") +(declare-function wttrin--debug-log "wttrin-debug") (defgroup wttrin nil "Emacs frontend for the weather web service wttr.in." @@ -119,9 +120,13 @@ Default is 900 seconds (15 minutes)." (defcustom wttrin-mode-line-startup-delay 3 "Seconds to delay initial mode-line weather fetch after Emacs starts. This allows network stack and daemon initialization to complete before -fetching weather data. Recommended range: 1-5 seconds." +fetching weather data. Must be between 1 and 10 seconds." :group 'wttrin - :type 'integer) + :type '(restricted-sexp :match-alternatives + ((lambda (val) + (and (integerp val) + (>= val 1) + (<= val 10)))))) (defcustom wttrin-mode-line-emoji-font "Noto Color Emoji" "Font family to use for mode-line weather emoji. @@ -147,7 +152,8 @@ display with `wttrin-mode-line-mode'." "Enable debug functions for troubleshooting wttrin behavior. When non-nil, loads wttrin-debug.el which provides: - Automatic mode-line diagnostic logging when wttrin runs -- Raw weather data saved to timestamped files in `temporary-file-directory' +- Raw weather data saved to timestamped files in variable + `temporary-file-directory' - Interactive debug commands for troubleshooting Set this to t BEFORE loading wttrin, typically in your init file: @@ -186,8 +192,8 @@ Set this to t BEFORE loading wttrin, typically in your init file: (define-key map [mode-line mouse-3] 'wttrin-mode-line-force-refresh) map) "Keymap for mode-line weather widget interactions. -Left-click (mouse-1): refresh weather and open buffer. -Right-click (mouse-3): force-refresh cache and update tooltip.") +Left-click: refresh weather and open buffer. +Right-click: force-refresh cache and update tooltip.") (defun wttrin-additional-url-params () "Concatenates extra information into the URL." @@ -302,8 +308,8 @@ Returns the path to the saved file." filepath)) (defun wttrin--validate-weather-data (raw-string) - "Return t if RAW-STRING contains valid weather data. -Returns nil if data is missing or contains errors." + "Check if RAW-STRING has valid weather data. +Return t if valid, nil if missing or contains errors." (not (or (null raw-string) (string-match "ERROR" raw-string)))) (defun wttrin--process-weather-content (raw-string) @@ -403,19 +409,27 @@ CALLBACK is called with the weather data string when ready, or nil on error." (funcall callback data)) (funcall callback nil)))))))) +(defun wttrin--get-cache-entries-by-age () + "Return list of (key . timestamp) pairs sorted oldest-first. +Extracts all cache entries and sorts them by timestamp in ascending order. +Returns a list where each element is a cons cell (key . timestamp)." + (let ((entries nil)) + (maphash (lambda (key value) + (push (cons key (car value)) entries)) ; car value = timestamp + wttrin--cache) + (sort entries (lambda (a b) (< (cdr a) (cdr b)))))) + (defun wttrin--cleanup-cache-if-needed () - "Remove old entries if cache exceeds max size." + "Remove oldest entries if cache exceeds max size. +Removes oldest entries based on `wttrin--cache-cleanup-percentage' +when cache count exceeds `wttrin-cache-max-entries'. +This creates headroom to avoid frequent cleanups." (when (> (hash-table-count wttrin--cache) wttrin-cache-max-entries) - (let ((entries nil)) - (maphash (lambda (k v) - (push (cons k (car v)) entries)) - wttrin--cache) - (setq entries (sort entries (lambda (a b) (< (cdr a) (cdr b))))) - ;; Remove oldest entries based on wttrin--cache-cleanup-percentage - (dotimes (_ (floor (* (length entries) wttrin--cache-cleanup-percentage))) - (when entries - (remhash (caar entries) wttrin--cache) - (setq entries (cdr entries))))))) + (let* ((entries-by-age (wttrin--get-cache-entries-by-age)) + (num-to-remove (floor (* (length entries-by-age) + wttrin--cache-cleanup-percentage)))) + (dotimes (i num-to-remove) + (remhash (car (nth i entries-by-age)) wttrin--cache))))) (defun wttrin-clear-cache () "Clear the weather cache." @@ -469,7 +483,8 @@ Uses wttr.in custom format for concise weather with emoji." (defun wttrin--mode-line-update-display (weather-string) "Update mode-line display with WEATHER-STRING. Extracts emoji for mode-line, stores full info for tooltip. -WEATHER-STRING format: \"Location: emoji temp conditions\" (e.g., \"Paris: ☀️ +61°F Clear\")." +WEATHER-STRING format: \"Location: emoji temp conditions\", +e.g., \"Paris: ☀️ +61°F Clear\"." (when (featurep 'wttrin-debug) (wttrin--debug-log "mode-line-display: Updating display with: %S" weather-string)) ;; Store full weather info for tooltip |
