diff options
| author | Craig Jennings <c@cjennings.net> | 2025-11-08 11:53:02 -0600 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2025-11-08 11:53:02 -0600 |
| commit | 1f40ef408641680c951a65b72be240d9b7729d8e (patch) | |
| tree | 083ae2d5ecf73134311233152f9dacc3f125fb77 | |
| parent | b44277b019ac64ffeacde0214cd3c9cd18014ba9 (diff) | |
feat: debug: add comprehensive debug logging and integration tests
Enhanced wttrin-debug.el:
- Added wttrin--debug-log() function for timestamped logging
- Added wttrin--debug-clear-log() to clear log
- Added wttrin--debug-show-log() to display log in buffer
- Debug log structure: list of (timestamp . message) pairs
Added debug logging to key functions in wttrin.el:
- wttrin--fetch-url: Logs start, success (bytes), and errors
- wttrin--mode-line-fetch-weather: Logs start, URL, data received
- wttrin--mode-line-update-display: Logs display update, emoji extraction
Created comprehensive integration tests:
- test-wttrin-integration-with-debug.el (5 tests, 3 passing)
- Tests fetch, mode-line display, error handling with debug logging
- Includes mocked network calls to avoid external dependencies
- Example debug output shows complete flow:
[wttrin-debug 11:51:46.490] mode-line-fetch: Starting fetch for Berkeley, CA
[wttrin-debug 11:51:46.490] mode-line-fetch: Received data = "Berkeley, CA: ☀️ +62°F Clear"
[wttrin-debug 11:51:46.490] mode-line-display: Extracted emoji = "☀", font = Noto Color Emoji
[wttrin-debug 11:51:46.490] mode-line-display: Complete. mode-line-string set = YES
Added test fixtures:
- tests/fixtures/test-init.el: Minimal config with debug enabled
- tests/README-DEBUG-TESTS.md: Documentation for using debug features
Usage:
(setq wttrin-debug t) ; Before loading wttrin
(require 'wttrin)
M-x wttrin--debug-show-log ; View all logged events
This provides complete visibility into wttrin's operation for troubleshooting.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
| -rw-r--r-- | tests/README-DEBUG-TESTS.md | 82 | ||||
| -rw-r--r-- | tests/fixtures/test-init.el | 22 | ||||
| -rw-r--r-- | tests/test-wttrin-integration-with-debug.el | 252 | ||||
| -rw-r--r-- | wttrin-debug.el | 33 | ||||
| -rw-r--r-- | wttrin.el | 39 |
5 files changed, 417 insertions, 11 deletions
diff --git a/tests/README-DEBUG-TESTS.md b/tests/README-DEBUG-TESTS.md new file mode 100644 index 0000000..7310123 --- /dev/null +++ b/tests/README-DEBUG-TESTS.md @@ -0,0 +1,82 @@ +# Wttrin Debug Integration Tests + +This directory contains comprehensive integration tests with debug logging enabled. + +## Running the Tests + +```bash +cd /path/to/wttrin +emacs --batch --eval "(progn + (package-initialize) + (add-to-list 'load-path \".\") + (setq wttrin-debug t) + (load-file \"wttrin.el\") + (load-file \"tests/test-wttrin-integration-with-debug.el\") + (ert-run-tests-batch-and-exit))" +``` + +## What the Tests Show + +The integration tests demonstrate: + +1. **Debug logging captures all key events**: + - URL fetch starting + - Data received (with byte count) + - Mode-line display updates + - Emoji extraction + - Error handling + +2. **Example debug output from a successful fetch**: +``` +[wttrin-debug 11:51:46.490] mode-line-fetch: Starting fetch for Berkeley, CA +[wttrin-debug 11:51:46.490] mode-line-fetch: URL = https://wttr.in/Berkeley%2C%20CA?m&format=%l:+%c+%t+%C +[wttrin-debug 11:51:46.490] mode-line-fetch: Received data = "Berkeley, CA: ☀️ +62°F Clear" +[wttrin-debug 11:51:46.490] mode-line-display: Updating display with: "Berkeley, CA: ☀️ +62°F Clear" +[wttrin-debug 11:51:46.490] mode-line-display: Extracted emoji = "☀", font = Noto Color Emoji +[wttrin-debug 11:51:46.490] mode-line-display: Complete. mode-line-string set = YES +``` + +## Using Debug Mode in Your Configuration + +### Enable Debug Before Loading + +```emacs-lisp +;; In your init.el, BEFORE (require 'wttrin): +(setq wttrin-debug t) +(require 'wttrin) +``` + +### Or Enable Later + +```emacs-lisp +M-x debug-wttrin-enable +``` + +### View Debug Log + +```emacs-lisp +M-x wttrin--debug-show-log +``` + +This opens a buffer showing all debug events with timestamps. + +### Clear Debug Log + +```emacs-lisp +M-x wttrin--debug-clear-log +``` + +## Test Fixtures + +- `fixtures/test-init.el` - Minimal init file with debug enabled for manual testing + +## Troubleshooting + +If wttrin isn't loading in your configuration: + +1. **Enable debug mode** (set `wttrin-debug` to `t` before loading) +2. **Check dependencies**: Run `M-x package-list-packages` and ensure `xterm-color` is installed +3. **View debug log**: Run `M-x wttrin--debug-show-log` after trying to use wttrin +4. **Check for errors**: Look in `*Messages*` buffer for any error messages + +The debug log will show you exactly where the process stops or fails. diff --git a/tests/fixtures/test-init.el b/tests/fixtures/test-init.el new file mode 100644 index 0000000..26e8395 --- /dev/null +++ b/tests/fixtures/test-init.el @@ -0,0 +1,22 @@ +;;; test-init.el --- Test fixture for wttrin integration tests -*- lexical-binding: t; -*- + +;; This is a minimal init.el for testing wttrin with debug enabled + +;; Enable debug mode BEFORE loading wttrin +(setq wttrin-debug t) + +;; Configure wttrin +(setq wttrin-default-locations '("Berkeley, CA" "New Orleans, LA")) +(setq wttrin-unit-system "m") ; Metric +(setq wttrin-mode-line-favorite-location "Berkeley, CA") +(setq wttrin-mode-line-startup-delay 0) ; No delay for tests +(setq wttrin-mode-line-refresh-interval 3600) ; 1 hour + +;; Load wttrin (assumes it's in load-path) +(require 'wttrin) + +;; Don't auto-enable mode-line in tests (we'll do it explicitly) +(setq wttrin-mode-line-auto-enable nil) + +(provide 'test-init) +;;; test-init.el ends here diff --git a/tests/test-wttrin-integration-with-debug.el b/tests/test-wttrin-integration-with-debug.el new file mode 100644 index 0000000..db4e05f --- /dev/null +++ b/tests/test-wttrin-integration-with-debug.el @@ -0,0 +1,252 @@ +;;; test-wttrin-integration-with-debug.el --- Integration test with debug enabled -*- lexical-binding: t; -*- + +;; Copyright (C) 2024 Craig Jennings + +;;; Commentary: +;; Comprehensive integration test that: +;; 1. Enables debug mode +;; 2. Mocks weather fetch to avoid network calls +;; 3. Tests mode-line display with real weather data +;; 4. Verifies debug log captures key events + +;;; Code: + +(require 'ert) +(require 'wttrin) + +;; Sample weather data from wttr.in custom format API +(defconst test-wttrin-sample-weather-data + "Berkeley, CA: ☀️ +62°F Clear" + "Sample weather data in wttr.in custom format.") + +(defconst test-wttrin-sample-full-weather + "Weather for Berkeley, CA + + \\ / Clear + .-. 62 °F + ― ( ) ― ↑ 5 mph + `-' 10 mi + / \\ 0.0 in" + "Sample full weather display data.") + +;;; Setup and Teardown + +(defun test-wttrin-setup () + "Set up test environment with debug enabled." + ;; Enable debug mode + (setq wttrin-debug t) + ;; Clear any existing debug log + (when (featurep 'wttrin-debug) + (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-unit-system "m")) + +(defun test-wttrin-teardown () + "Clean up after tests." + (when (boundp 'wttrin-mode-line-mode) + (wttrin-mode-line-mode -1)) + (wttrin-clear-cache) + (when (featurep 'wttrin-debug) + (wttrin--debug-clear-log)) + (setq wttrin-mode-line-string nil) + (setq wttrin--mode-line-tooltip-data nil)) + +;;; Mock URL Fetching + +(defvar test-wttrin--original-fetch-url nil + "Original wttrin--fetch-url function for restoration after test.") + +(defun test-wttrin-mock-fetch (url callback) + "Mock version of wttrin--fetch-url that returns fake weather data. +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)))) + +(defmacro with-mocked-fetch (&rest body) + "Execute BODY with wttrin--fetch-url mocked to return test data." + `(let ((test-wttrin--original-fetch-url (symbol-function 'wttrin--fetch-url))) + (unwind-protect + (progn + (fset 'wttrin--fetch-url #'test-wttrin-mock-fetch) + ,@body) + (fset 'wttrin--fetch-url test-wttrin--original-fetch-url)))) + +;;; Integration Tests + +(ert-deftest test-wttrin-debug-integration-mode-line-fetch-and-display () + "Integration test: Fetch weather and verify mode-line display with debug logging." + (test-wttrin-setup) + (unwind-protect + (with-mocked-fetch + ;; Clear debug log + (wttrin--debug-clear-log) + + ;; Fetch weather for mode-line + (wttrin--mode-line-fetch-weather) + + ;; Wait for async callback to complete (mocked, so should be fast) + (sleep-for 0.1) + + ;; Verify mode-line string was set + (should wttrin-mode-line-string) + (should (stringp wttrin-mode-line-string)) + (should (string-match-p "☀" wttrin-mode-line-string)) ; Should contain emoji + + ;; Verify tooltip data was set + (should wttrin--mode-line-tooltip-data) + (should (string= test-wttrin-sample-weather-data wttrin--mode-line-tooltip-data)) + + ;; Verify debug log captured key events + (let ((log-messages (mapcar #'cdr wttrin--debug-log))) + ;; Should have logged the fetch start + (should (seq-some (lambda (msg) (string-match-p "mode-line-fetch: Starting" msg)) + log-messages)) + ;; Should have logged receiving data + (should (seq-some (lambda (msg) (string-match-p "mode-line-fetch: Received data" msg)) + log-messages)) + ;; Should have logged display update + (should (seq-some (lambda (msg) (string-match-p "mode-line-display:" msg)) + log-messages)) + ;; Should have logged emoji extraction + (should (seq-some (lambda (msg) (string-match-p "Extracted emoji" msg)) + log-messages)))) + (test-wttrin-teardown))) + +(ert-deftest test-wttrin-debug-integration-full-weather-query () + "Integration test: Query full weather and verify debug logging." + (test-wttrin-setup) + (unwind-protect + (progn + ;; Clear debug log + (wttrin--debug-clear-log) + + ;; Mock full weather fetch + (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)))))) + + ;; Start the query (async, so we'll check results after delay) + (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 + (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)) + log-messages))))) + ;; Cleanup + (when (get-buffer "*wttr.in*") + (kill-buffer "*wttr.in*")) + (test-wttrin-teardown))) + +(ert-deftest test-wttrin-debug-integration-mode-line-mode-toggle () + "Integration test: Toggle mode-line mode and verify debug logging." + (test-wttrin-setup) + (unwind-protect + (with-mocked-fetch + ;; Clear debug 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) + + ;; Verify mode-line string is set + (should wttrin-mode-line-string) + + ;; Verify global-mode-string contains our widget + (should (member 'wttrin-mode-line-string global-mode-string)) + + ;; Disable mode-line mode + (wttrin-mode-line-mode -1) + (should-not wttrin-mode-line-mode) + + ;; Verify mode-line string is cleared + (should-not wttrin-mode-line-string) + + ;; Verify removed from global-mode-string + (should-not (member 'wttrin-mode-line-string global-mode-string))) + (test-wttrin-teardown))) + +(ert-deftest test-wttrin-debug-integration-error-handling () + "Integration test: Verify debug logging captures errors correctly." + (test-wttrin-setup) + (unwind-protect + (progn + ;; Clear debug log + (wttrin--debug-clear-log) + + ;; Mock fetch that returns nil (simulating network error) + (cl-letf (((symbol-function 'wttrin--fetch-url) + (lambda (url callback) + (when (featurep 'wttrin-debug) + (wttrin--debug-log "MOCK-FETCH: Simulating error for URL: %s" url)) + (run-at-time 0 nil (lambda () (funcall callback nil)))))) + + ;; Try to fetch (should handle error gracefully) + (wttrin--mode-line-fetch-weather) + + ;; Wait for async callback + (sleep-for 0.1) + + ;; Verify error was logged + (let ((log-messages (mapcar #'cdr wttrin--debug-log))) + (should (seq-some (lambda (msg) (string-match-p "No data received" msg)) + log-messages))))) + (test-wttrin-teardown))) + +(ert-deftest test-wttrin-debug-integration-log-inspection () + "Integration test: Verify debug log can be inspected programmatically." + (test-wttrin-setup) + (unwind-protect + (progn + ;; Clear and add some test log entries + (wttrin--debug-clear-log) + (wttrin--debug-log "Test message 1") + (wttrin--debug-log "Test message 2 with arg: %s" "value") + + ;; Verify log structure + (should (= 2 (length wttrin--debug-log))) + (should (consp (car wttrin--debug-log))) ; Each entry is (timestamp . message) + (should (stringp (caar wttrin--debug-log))) ; Timestamp is string + (should (stringp (cdar wttrin--debug-log))) ; Message is string + + ;; Verify messages + (let ((messages (mapcar #'cdr wttrin--debug-log))) + (should (member "Test message 1" messages)) + (should (seq-some (lambda (msg) (string-match-p "Test message 2.*value" msg)) + messages))) + + ;; Clear log + (wttrin--debug-clear-log) + (should (= 0 (length wttrin--debug-log)))) + (test-wttrin-teardown))) + +(provide 'test-wttrin-integration-with-debug) +;;; test-wttrin-integration-with-debug.el ends here diff --git a/wttrin-debug.el b/wttrin-debug.el index 55546d4..0ddecab 100644 --- a/wttrin-debug.el +++ b/wttrin-debug.el @@ -103,5 +103,38 @@ This function is called automatically when wttrin runs if debug mode is enabled. It creates the *wttrin-mode-debug* buffer with diagnostic information." (debug-wttrin-mode-line)) +(defvar wttrin--debug-log nil + "List of debug log entries. Each entry is (timestamp . message).") + +(defun wttrin--debug-log (format-string &rest args) + "Log a debug message if wttrin-debug is enabled. +FORMAT-STRING and ARGS are passed to `format'." + (when wttrin-debug + (let ((msg (apply #'format format-string args)) + (timestamp (format-time-string "%H:%M:%S.%3N"))) + (push (cons timestamp msg) wttrin--debug-log) + (message "[wttrin-debug %s] %s" timestamp msg)))) + +(defun wttrin--debug-clear-log () + "Clear the debug log." + (interactive) + (setq wttrin--debug-log nil) + (message "Wttrin debug log cleared")) + +;;;###autoload +(defun wttrin--debug-show-log () + "Display the wttrin debug log in a buffer." + (interactive) + (with-current-buffer (get-buffer-create "*wttrin-debug-log*") + (erase-buffer) + (insert "=== WTTRIN DEBUG LOG ===\n") + (insert (format "Total entries: %d\n\n" (length wttrin--debug-log))) + (if wttrin--debug-log + (dolist (entry (reverse wttrin--debug-log)) + (insert (format "[%s] %s\n" (car entry) (cdr entry)))) + (insert "(No log entries yet)\n")) + (goto-char (point-min)) + (display-buffer (current-buffer)))) + (provide 'wttrin-debug) ;;; wttrin-debug.el ends here @@ -209,6 +209,8 @@ This is a pure function with no side effects, suitable for testing." "Asynchronously fetch URL and call CALLBACK with decoded response. CALLBACK is called with the weather data string when ready, or nil on error. Handles header skipping, UTF-8 decoding, and error handling automatically." + (when (featurep 'wttrin-debug) + (wttrin--debug-log "wttrin--fetch-url: Starting fetch for URL: %s" url)) (let ((url-request-extra-headers (list wttrin-default-languages)) (url-user-agent "curl")) (url-retrieve @@ -218,6 +220,9 @@ Handles header skipping, UTF-8 decoding, and error handling automatically." (condition-case err (if (plist-get status :error) (progn + (when (featurep 'wttrin-debug) + (wttrin--debug-log "wttrin--fetch-url: Network error - %s" + (cdr (plist-get status :error)))) (message "wttrin: Network error - %s" (cdr (plist-get status :error))) (setq data nil)) (unwind-protect @@ -227,9 +232,15 @@ Handles header skipping, UTF-8 decoding, and error handling automatically." (re-search-forward "\r?\n\r?\n" nil t) (setq data (decode-coding-string (buffer-substring-no-properties (point) (point-max)) - 'utf-8))) + 'utf-8)) + (when (featurep 'wttrin-debug) + (wttrin--debug-log "wttrin--fetch-url: Successfully fetched %d bytes" + (length data)))) (kill-buffer (current-buffer)))) (error + (when (featurep 'wttrin-debug) + (wttrin--debug-log "wttrin--fetch-url: Error processing response - %s" + (error-message-string err))) (message "wttrin: Error processing response - %s" (error-message-string err)) (setq data nil))) (funcall callback data)))))) @@ -430,7 +441,7 @@ CALLBACK is called with the weather data string when ready, or nil on error." "Fetch weather for favorite location and update mode-line display. Uses wttr.in custom format for concise weather with emoji." (when (featurep 'wttrin-debug) - (message "wttrin mode-line: Fetching weather for %s" wttrin-mode-line-favorite-location)) + (wttrin--debug-log "mode-line-fetch: Starting fetch for %s" wttrin-mode-line-favorite-location)) (when wttrin-mode-line-favorite-location (let* ((location wttrin-mode-line-favorite-location) ;; Custom format: location + emoji + temp + conditions @@ -443,22 +454,24 @@ Uses wttr.in custom format for concise weather with emoji." (url-hexify-string location) format-params))) (when (featurep 'wttrin-debug) - (message "wttrin mode-line: URL = %s" url)) + (wttrin--debug-log "mode-line-fetch: URL = %s" url)) (wttrin--fetch-url url (lambda (data) - (when data - (let ((trimmed-data (string-trim data))) - (when (featurep 'wttrin-debug) - (message "wttrin mode-line: Received data = %S" trimmed-data)) - (wttrin--mode-line-update-display trimmed-data)))))))) + (if data + (let ((trimmed-data (string-trim data))) + (when (featurep 'wttrin-debug) + (wttrin--debug-log "mode-line-fetch: Received data = %S" trimmed-data)) + (wttrin--mode-line-update-display trimmed-data)) + (when (featurep 'wttrin-debug) + (wttrin--debug-log "mode-line-fetch: No data received (network error)")))))))) (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\")." (when (featurep 'wttrin-debug) - (message "wttrin mode-line: Updating display with: %S" weather-string)) + (wttrin--debug-log "mode-line-display: Updating display with: %S" weather-string)) ;; Store full weather info for tooltip (setq wttrin--mode-line-tooltip-data weather-string) ;; Extract just the emoji for mode-line display @@ -473,6 +486,9 @@ WEATHER-STRING format: \"Location: emoji temp conditions\" (e.g., \"Paris: ☀ 'face (list :family wttrin-mode-line-emoji-font :height 1.0)) emoji))) + (when (featurep 'wttrin-debug) + (wttrin--debug-log "mode-line-display: Extracted emoji = %S, font = %s" + emoji wttrin-mode-line-emoji-font)) (setq wttrin-mode-line-string (propertize (concat " " emoji-with-font) 'help-echo (lambda (_window _object _pos) @@ -483,8 +499,9 @@ WEATHER-STRING format: \"Location: emoji temp conditions\" (e.g., \"Paris: ☀ 'local-map wttrin--mode-line-map))) (force-mode-line-update t) (when (featurep 'wttrin-debug) - (message "wttrin mode-line: Display updated, mode-line-string = %S, tooltip = %S" - wttrin-mode-line-string wttrin--mode-line-tooltip-data))) + (wttrin--debug-log "mode-line-display: Complete. mode-line-string set = %s, tooltip = %S" + (if wttrin-mode-line-string "YES" "NO") + wttrin--mode-line-tooltip-data))) (defun wttrin-mode-line-click () "Handle left-click on mode-line weather widget. |
