aboutsummaryrefslogtreecommitdiff
path: root/tests/test-wttrin-ansi-color-rendering.el
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-02-17 19:14:14 -0600
committerCraig Jennings <c@cjennings.net>2026-02-17 19:14:14 -0600
commit28b7e4cecadce207d532b8d42346c3823450a35f (patch)
tree4d8772206e10c59762ae1e60343d4bc8dded77b5 /tests/test-wttrin-ansi-color-rendering.el
parentbf989bb594680eb2e3b69f55752353aa33cb47bb (diff)
downloademacs-wttrin-28b7e4cecadce207d532b8d42346c3823450a35f.tar.gz
emacs-wttrin-28b7e4cecadce207d532b8d42346c3823450a35f.zip
refactor: tests: standardize naming, consolidate files, and expand testutil
- Expand testutil-wttrin.el with shared fixtures and macros (testutil-wttrin-with-clean-weather-buffer, testutil-wttrin-mock-http-response, sample ANSI/weather constants) - Consolidate cache tests: port unique tests from cleanup-cache-constants and cleanup-cache-refactored into cleanup-cache-if-needed, delete obsolete files - Extract startup-delay tests into dedicated file - Add setup/teardown and (require 'testutil-wttrin) to all test files - Rename all 160 tests to follow test-<module>-<category>-<scenario>-<expected-result> convention - Rename integration test file and add detailed docstrings - Update Makefile glob to discover new integration test naming pattern Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'tests/test-wttrin-ansi-color-rendering.el')
-rw-r--r--tests/test-wttrin-ansi-color-rendering.el388
1 files changed, 161 insertions, 227 deletions
diff --git a/tests/test-wttrin-ansi-color-rendering.el b/tests/test-wttrin-ansi-color-rendering.el
index 656971b..30c5a4d 100644
--- a/tests/test-wttrin-ansi-color-rendering.el
+++ b/tests/test-wttrin-ansi-color-rendering.el
@@ -14,17 +14,6 @@
(require 'xterm-color)
(require 'testutil-wttrin)
-;;; Test Data - Realistic wttr.in response with ANSI codes
-
-(defconst test-wttrin-ansi-sample-with-colors
- "Weather report: Paris
-
-\x1b[38;5;226m \\ /\x1b[0m Partly cloudy
-\x1b[38;5;226m _ /\"\"\x1b[38;5;250m.-.\x1b[0m \x1b[38;5;118m+13\x1b[0m(\x1b[38;5;082m12\x1b[0m) °C
-\x1b[38;5;226m \\_\x1b[38;5;250m( ). \x1b[0m ↑ \x1b[38;5;190m12\x1b[0m km/h
-"
- "Sample weather data with ANSI color codes (escape sequences).")
-
;;; Test Setup and Teardown
(defun test-wttrin-ansi-setup ()
@@ -64,28 +53,28 @@ Returns cons (colored-chars . total-chars)."
;;; Tests
-(ert-deftest test-wttrin-ansi-xterm-color-filter-processes-codes ()
+(ert-deftest test-wttrin-ansi-normal-xterm-color-filter-removes-escape-codes ()
"Test that xterm-color-filter properly processes ANSI codes."
(test-wttrin-ansi-setup)
(unwind-protect
- (let ((filtered (xterm-color-filter test-wttrin-ansi-sample-with-colors)))
+ (let ((filtered (xterm-color-filter testutil-wttrin-sample-ansi-response)))
;; After filtering, ANSI escape codes should be removed
(should-not (test-wttrin-ansi--has-ansi-escape-codes filtered))
;; The filtered text should be shorter (escape codes removed)
- (should (< (length filtered) (length test-wttrin-ansi-sample-with-colors)))
+ (should (< (length filtered) (length testutil-wttrin-sample-ansi-response)))
;; Text should still contain the actual weather content
(should (string-match-p "Paris" filtered))
(should (string-match-p "Partly cloudy" filtered)))
(test-wttrin-ansi-teardown)))
-(ert-deftest test-wttrin-ansi-display-weather-renders-colors ()
+(ert-deftest test-wttrin-ansi-normal-display-weather-renders-colored-text ()
"Test that display-weather properly renders ANSI colors in buffer."
(test-wttrin-ansi-setup)
(unwind-protect
- (progn
- (wttrin--display-weather "Paris" test-wttrin-ansi-sample-with-colors)
+ (testutil-wttrin-with-clean-weather-buffer
+ (wttrin--display-weather "Paris" testutil-wttrin-sample-ansi-response)
(should (get-buffer "*wttr.in*"))
@@ -104,91 +93,85 @@ Returns cons (colored-chars . total-chars)."
(colored (car counts))
(total (cdr counts)))
;; At least some characters should have color properties
- ;; (not all white text)
(should (> colored 0))
-
;; Colored text should be a reasonable portion (not just 2 chars)
- ;; With ANSI codes, we expect at least 10% of text to be colored
(should (> colored (/ total 10))))))
(test-wttrin-ansi-teardown)))
-(ert-deftest test-wttrin-ansi-mode-line-click-renders-colors ()
+(ert-deftest test-wttrin-ansi-normal-mode-line-click-renders-colored-text ()
"Test that clicking mode-line icon renders colors properly.
This reproduces the bug where mode-line click shows white text."
(test-wttrin-ansi-setup)
(unwind-protect
- (let* ((location "Paris")
- (cache-key (wttrin--make-cache-key location))
- (now 1000.0)
- (wttrin-favorite-location location))
-
- ;; Mock the async fetch to return ANSI-coded data
- (cl-letf (((symbol-function 'float-time)
- (lambda () now))
- ((symbol-function 'wttrin-fetch-raw-string)
- (lambda (_location callback)
- (funcall callback test-wttrin-ansi-sample-with-colors)))
- ((symbol-function 'wttrin--cleanup-cache-if-needed)
- (lambda () nil)))
-
- ;; Simulate mode-line click by calling wttrin
- ;; (wttrin-mode-line-click just calls this)
- (wttrin location)
-
- ;; Give async callback time to execute
- ;; (In real execution this is async, but our mock is synchronous)
- (should (get-buffer "*wttr.in*"))
-
- (with-current-buffer "*wttr.in*"
- ;; Buffer should not contain raw ANSI codes
- (let ((buffer-text (buffer-substring-no-properties (point-min) (point-max))))
- (should-not (test-wttrin-ansi--has-ansi-escape-codes buffer-text)))
-
- ;; Check that text has color properties (not all white)
- (let* ((counts (test-wttrin-ansi--count-colored-text))
- (colored (car counts)))
- ;; Should have colored text (proving colors are rendered)
- (should (> colored 0))))))
+ (testutil-wttrin-with-clean-weather-buffer
+ (let* ((location "Paris")
+ (cache-key (wttrin--make-cache-key location))
+ (now 1000.0)
+ (wttrin-favorite-location location))
+
+ ;; Mock the async fetch to return ANSI-coded data
+ (cl-letf (((symbol-function 'float-time)
+ (lambda () now))
+ ((symbol-function 'wttrin-fetch-raw-string)
+ (lambda (_location callback)
+ (funcall callback testutil-wttrin-sample-ansi-response)))
+ ((symbol-function 'wttrin--cleanup-cache-if-needed)
+ (lambda () nil)))
+
+ (wttrin location)
+
+ (should (get-buffer "*wttr.in*"))
+
+ (with-current-buffer "*wttr.in*"
+ ;; Buffer should not contain raw ANSI codes
+ (let ((buffer-text (buffer-substring-no-properties (point-min) (point-max))))
+ (should-not (test-wttrin-ansi--has-ansi-escape-codes buffer-text)))
+
+ ;; Check that text has color properties (not all white)
+ (let* ((counts (test-wttrin-ansi--count-colored-text))
+ (colored (car counts)))
+ (should (> colored 0)))))))
(test-wttrin-ansi-teardown)))
-(ert-deftest test-wttrin-ansi-cached-data-preserves-colors ()
+(ert-deftest test-wttrin-ansi-normal-cached-data-preserves-color-properties ()
"Test that cached weather data preserves color information.
Verifies that cache doesn't strip ANSI codes or color properties."
(test-wttrin-ansi-setup)
(unwind-protect
- (let* ((location "Berlin")
- (cache-key (wttrin--make-cache-key location))
- (now 1000.0))
-
- ;; Pre-populate cache with ANSI-coded data
- (puthash cache-key (cons now test-wttrin-ansi-sample-with-colors)
- wttrin--cache)
-
- (cl-letf (((symbol-function 'float-time)
- (lambda () (+ now 100.0)))) ; Within TTL
-
- ;; Call wttrin - should use cached data
- (wttrin location)
-
- (should (get-buffer "*wttr.in*"))
-
- (with-current-buffer "*wttr.in*"
- ;; Even with cached data, colors should be rendered
- (let* ((counts (test-wttrin-ansi--count-colored-text))
- (colored (car counts))
- (total (cdr counts)))
- (should (> colored 0))
- ;; Should have reasonable color coverage
- (should (> colored (/ total 10)))))))
+ (testutil-wttrin-with-clean-weather-buffer
+ (let* ((location "Berlin")
+ (cache-key (wttrin--make-cache-key location))
+ (now 1000.0))
+
+ ;; Pre-populate cache with ANSI-coded data
+ (puthash cache-key (cons now testutil-wttrin-sample-ansi-response)
+ wttrin--cache)
+
+ (cl-letf (((symbol-function 'float-time)
+ (lambda () (+ now 100.0)))) ; Within TTL
+
+ ;; Call wttrin - should use cached data
+ (wttrin location)
+
+ (should (get-buffer "*wttr.in*"))
+
+ (with-current-buffer "*wttr.in*"
+ ;; Even with cached data, colors should be rendered
+ (let* ((counts (test-wttrin-ansi--count-colored-text))
+ (colored (car counts))
+ (total (cdr counts)))
+ (should (> colored 0))
+ ;; Should have reasonable color coverage
+ (should (> colored (/ total 10))))))))
(test-wttrin-ansi-teardown)))
-(ert-deftest test-wttrin-ansi-buffer-face-mode-doesnt-strip-colors ()
+(ert-deftest test-wttrin-ansi-boundary-buffer-face-mode-preserves-colors ()
"Test that buffer-face-mode doesn't strip xterm-color face properties.
This reproduces the bug where weather buffer shows mostly white text."
(test-wttrin-ansi-setup)
(unwind-protect
- (progn
- (wttrin--display-weather "Paris" test-wttrin-ansi-sample-with-colors)
+ (testutil-wttrin-with-clean-weather-buffer
+ (wttrin--display-weather "Paris" testutil-wttrin-sample-ansi-response)
(should (get-buffer "*wttr.in*"))
@@ -204,151 +187,122 @@ This reproduces the bug where weather buffer shows mostly white text."
(* 100.0 (/ (float colored) total))
0)))
;; Should have substantial colored text (>10%)
- ;; If this fails with ~5% or less, buffer-face-mode is interfering
(should (> percentage 10.0))
-
- ;; With face-remap-add-relative fix, we get ~30-40 colored chars
- ;; (our test data is small, so absolute count is low)
- ;; The key is the percentage, not absolute count
(should (> colored 20)))))
(test-wttrin-ansi-teardown)))
-(ert-deftest test-wttrin-ansi-mode-line-doesnt-pollute-main-cache ()
+(ert-deftest test-wttrin-ansi-boundary-mode-line-plain-text-cache-shows-content ()
"Test that mode-line weather fetch doesn't pollute main cache with plain text.
This reproduces the bug where clicking mode-line shows white text."
(test-wttrin-ansi-setup)
(unwind-protect
- (let* ((location "Berlin")
- (cache-key (wttrin--make-cache-key location))
- (now 1000.0)
- (plain-text-weather "Berlin: ☀️ +20°C Clear") ; No ANSI codes
- (ansi-weather test-wttrin-ansi-sample-with-colors)) ; Has ANSI codes
-
- ;; Simulate mode-line storing plain-text in cache (the bug)
- ;; This shouldn't happen, but let's verify the system is resilient
- (puthash cache-key (cons now plain-text-weather) wttrin--cache)
-
- (cl-letf (((symbol-function 'float-time)
- (lambda () (+ now 100.0))) ; Within TTL, will use cache
- ((symbol-function 'wttrin-fetch-raw-string)
- (lambda (_location callback)
- ;; Should never be called since cache is fresh
- (error "Should not fetch when cache is fresh"))))
-
- ;; Call wttrin - it will use cached data
- (wttrin location)
-
- (should (get-buffer "*wttr.in*"))
-
- (with-current-buffer "*wttr.in*"
- ;; Even if cache has plain text, should we handle it gracefully?
- ;; At minimum, buffer should exist and not error
- (should (> (buffer-size) 0))
-
- ;; The real fix: mode-line should use separate cache or namespace
- ;; For now, document that plain-text cache = no colors
- (let* ((buffer-text (buffer-substring-no-properties (point-min) (point-max))))
- ;; Should contain the weather data (even if not colored)
- (should (string-match-p "Berlin" buffer-text))))))
+ (testutil-wttrin-with-clean-weather-buffer
+ (let* ((location "Berlin")
+ (cache-key (wttrin--make-cache-key location))
+ (now 1000.0)
+ (plain-text-weather "Berlin: ☀️ +20°C Clear")) ; No ANSI codes
+
+ ;; Simulate mode-line storing plain-text in cache (the bug)
+ (puthash cache-key (cons now plain-text-weather) wttrin--cache)
+
+ (cl-letf (((symbol-function 'float-time)
+ (lambda () (+ now 100.0))) ; Within TTL, will use cache
+ ((symbol-function 'wttrin-fetch-raw-string)
+ (lambda (_location callback)
+ (error "Should not fetch when cache is fresh"))))
+
+ (wttrin location)
+
+ (should (get-buffer "*wttr.in*"))
+
+ (with-current-buffer "*wttr.in*"
+ (should (> (buffer-size) 0))
+ (let* ((buffer-text (buffer-substring-no-properties (point-min) (point-max))))
+ (should (string-match-p "Berlin" buffer-text)))))))
(test-wttrin-ansi-teardown)))
-(ert-deftest test-wttrin-ansi-full-scenario-mode-line-then-click ()
+(ert-deftest test-wttrin-ansi-normal-full-scenario-mode-line-then-click-has-colors ()
"Test full scenario: mode-line fetch, then user clicks to open buffer.
This is the exact user workflow that exposes the bug."
(test-wttrin-ansi-setup)
(unwind-protect
- (let* ((location "Tokyo")
- (now 1000.0)
- (wttrin-favorite-location location)
- (mode-line-fetch-count 0)
- (main-fetch-count 0))
-
- (cl-letf (((symbol-function 'float-time)
- (lambda () now))
- ((symbol-function 'url-retrieve)
- (lambda (url callback)
- ;; Mode-line uses url-retrieve directly
- (setq mode-line-fetch-count (1+ mode-line-fetch-count))
- ;; Simulate async callback with plain-text format
- (with-temp-buffer
- (insert "HTTP/1.1 200 OK\n\n")
- (insert "Tokyo: ☀️ +25°C Clear") ; Plain text, no ANSI
- (funcall callback nil))))
- ((symbol-function 'wttrin-fetch-raw-string)
- (lambda (_location callback)
- ;; Main buffer fetch should get ANSI codes
- (setq main-fetch-count (1+ main-fetch-count))
- (funcall callback test-wttrin-ansi-sample-with-colors)))
- ((symbol-function 'wttrin--cleanup-cache-if-needed)
- (lambda () nil)))
-
- ;; Step 1: Mode-line fetches weather (simulated)
- (wttrin--mode-line-fetch-weather)
- ;; Mode-line should have fetched
- (should (= mode-line-fetch-count 1))
-
- ;; Step 2: User clicks mode-line icon (calls wttrin)
- (wttrin location)
- ;; Main fetch should have happened (cache miss)
- (should (= main-fetch-count 1))
-
- ;; Step 3: Verify buffer has colors
- (should (get-buffer "*wttr.in*"))
-
- (with-current-buffer "*wttr.in*"
- (let* ((counts (test-wttrin-ansi--count-colored-text))
- (colored (car counts))
- (total (cdr counts)))
- ;; Should have colored text
- (should (> colored 0))
- ;; Should be substantial (>10%)
- (should (> colored (/ total 10)))))))
+ (testutil-wttrin-with-clean-weather-buffer
+ (let* ((location "Tokyo")
+ (now 1000.0)
+ (wttrin-favorite-location location)
+ (mode-line-fetch-count 0)
+ (main-fetch-count 0))
+
+ (cl-letf (((symbol-function 'float-time)
+ (lambda () now))
+ ((symbol-function 'url-retrieve)
+ (lambda (url callback)
+ (setq mode-line-fetch-count (1+ mode-line-fetch-count))
+ (with-temp-buffer
+ (insert "HTTP/1.1 200 OK\n\n")
+ (insert "Tokyo: ☀️ +25°C Clear")
+ (funcall callback nil))))
+ ((symbol-function 'wttrin-fetch-raw-string)
+ (lambda (_location callback)
+ (setq main-fetch-count (1+ main-fetch-count))
+ (funcall callback testutil-wttrin-sample-ansi-response)))
+ ((symbol-function 'wttrin--cleanup-cache-if-needed)
+ (lambda () nil)))
+
+ ;; Step 1: Mode-line fetches weather (simulated)
+ (wttrin--mode-line-fetch-weather)
+ (should (= mode-line-fetch-count 1))
+
+ ;; Step 2: User clicks mode-line icon (calls wttrin)
+ (wttrin location)
+ (should (= main-fetch-count 1))
+
+ ;; Step 3: Verify buffer has colors
+ (should (get-buffer "*wttr.in*"))
+
+ (with-current-buffer "*wttr.in*"
+ (let* ((counts (test-wttrin-ansi--count-colored-text))
+ (colored (car counts))
+ (total (cdr counts)))
+ (should (> colored 0))
+ (should (> colored (/ total 10))))))))
(test-wttrin-ansi-teardown)))
-(ert-deftest test-wttrin-ansi-cache-stores-ansi-codes ()
+(ert-deftest test-wttrin-ansi-normal-cache-stores-raw-ansi-codes ()
"Test that cache stores raw ANSI codes, not filtered text.
This verifies the cache workflow preserves ANSI codes for later filtering."
(test-wttrin-ansi-setup)
(unwind-protect
- (let* ((location "Vienna")
- (cache-key (wttrin--make-cache-key location))
- (now 1000.0)
- (fetch-count 0))
-
- (cl-letf (((symbol-function 'float-time)
- (lambda () now))
- ((symbol-function 'wttrin-fetch-raw-string)
- (lambda (_location callback)
- (setq fetch-count (1+ fetch-count))
- ;; Simulate fetch returning ANSI codes
- (funcall callback test-wttrin-ansi-sample-with-colors)))
- ((symbol-function 'wttrin--cleanup-cache-if-needed)
- (lambda () nil)))
-
- ;; Fetch weather (will cache the result)
- (wttrin location)
-
- ;; Verify fetch was called
- (should (= fetch-count 1))
-
- ;; Check what's in the cache
- (let* ((cached (gethash cache-key wttrin--cache))
- (cached-data (cdr cached)))
-
- ;; Cache should have data
- (should cached)
- (should cached-data)
-
- ;; CRITICAL: Cache should store RAW ANSI codes
- ;; NOT filtered text with face properties
- (should (stringp cached-data))
- (should (string-match-p "\x1b\\[" cached-data))
-
- ;; Verify it's the original ANSI string
- (should (string-match-p "Partly cloudy" cached-data)))))
+ (testutil-wttrin-with-clean-weather-buffer
+ (let* ((location "Vienna")
+ (cache-key (wttrin--make-cache-key location))
+ (now 1000.0)
+ (fetch-count 0))
+
+ (cl-letf (((symbol-function 'float-time)
+ (lambda () now))
+ ((symbol-function 'wttrin-fetch-raw-string)
+ (lambda (_location callback)
+ (setq fetch-count (1+ fetch-count))
+ (funcall callback testutil-wttrin-sample-ansi-response)))
+ ((symbol-function 'wttrin--cleanup-cache-if-needed)
+ (lambda () nil)))
+
+ (wttrin location)
+
+ (should (= fetch-count 1))
+
+ (let* ((cached (gethash cache-key wttrin--cache))
+ (cached-data (cdr cached)))
+ (should cached)
+ (should cached-data)
+ ;; CRITICAL: Cache should store RAW ANSI codes
+ (should (stringp cached-data))
+ (should (string-match-p "\x1b\\[" cached-data))
+ (should (string-match-p "Partly cloudy" cached-data))))))
(test-wttrin-ansi-teardown)))
-(ert-deftest test-wttrin-ansi-fetch-returns-ansi-codes ()
+(ert-deftest test-wttrin-ansi-normal-fetch-returns-raw-ansi-codes ()
"Test that wttrin-fetch-raw-string returns data with ANSI codes.
This verifies the fetch function returns unfiltered data from wttr.in."
(test-wttrin-ansi-setup)
@@ -356,38 +310,21 @@ This verifies the fetch function returns unfiltered data from wttr.in."
(let ((location "Prague")
(callback-data nil)
(url-retrieve-called nil)
- (wttrin-unit-system "u")) ; Set unit system for URL generation
+ (wttrin-unit-system "u"))
;; Mock url-retrieve to simulate wttr.in response
- (cl-letf (((symbol-function 'url-retrieve)
- (lambda (url callback)
- (setq url-retrieve-called t)
- ;; Verify URL has ANSI format flag
- ;; wttr.in uses ?mA, ?uA, or ?A for ANSI colored output
- (should (or (string-match-p "\\?mA" url)
- (string-match-p "\\?uA" url)
- (string-match-p "\\?A" url)))
- ;; Simulate HTTP response with ANSI codes
- (with-temp-buffer
- (insert "HTTP/1.1 200 OK\n\n")
- (insert test-wttrin-ansi-sample-with-colors)
- (funcall callback nil)))))
-
- ;; Call fetch
+ (testutil-wttrin-mock-http-response testutil-wttrin-sample-ansi-response
(wttrin-fetch-raw-string
location
(lambda (data)
(setq callback-data data)))
- ;; Verify url-retrieve was called
- (should url-retrieve-called)
-
;; Verify callback received ANSI codes
(should callback-data)
(should (string-match-p "\x1b\\[" callback-data))))
(test-wttrin-ansi-teardown)))
-(ert-deftest test-wttrin-ansi-build-url-includes-ansi-flag ()
+(ert-deftest test-wttrin-ansi-normal-build-url-includes-ansi-flag ()
"Test that wttrin--build-url includes ANSI color flag in URL.
The 'A' flag tells wttr.in to include ANSI color codes in the response."
(test-wttrin-ansi-setup)
@@ -396,19 +333,16 @@ The 'A' flag tells wttr.in to include ANSI color codes in the response."
;; Test with metric system
(let ((wttrin-unit-system "m"))
(let ((url (wttrin--build-url "Berlin")))
- ;; Should have ?mA flag (metric + ANSI)
(should (string-match-p "\\?mA" url))))
;; Test with USCS system
(let ((wttrin-unit-system "u"))
(let ((url (wttrin--build-url "London")))
- ;; Should have ?uA flag (USCS + ANSI)
(should (string-match-p "\\?uA" url))))
;; Test with no unit system
(let ((wttrin-unit-system nil))
(let ((url (wttrin--build-url "Paris")))
- ;; Should have just ?A flag (ANSI)
(should (string-match-p "\\?A" url)))))
(test-wttrin-ansi-teardown)))