aboutsummaryrefslogtreecommitdiff
path: root/tests/test-wttrin-mode-initialization-order.el
blob: 6554b6466cda0d5a2fe4ab0a71830156e31fb956 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
;;; test-wttrin-mode-initialization-order.el --- Test mode initialization order -*- lexical-binding: t; -*-

;; Copyright (C) 2025-2026 Craig Jennings

;;; Commentary:

;; This test verifies that wttrin--display-weather initializes wttrin-mode
;; BEFORE making `xterm-color--state' buffer-local.  This prevents
;; kill-all-local-variables (called by derived modes) from wiping out
;; important state.
;;
;; Bug context: On fresh Emacs launch, weather displayed with no colors because
;; xterm-color--state was set buffer-local BEFORE wttrin-mode was called, and
;; wttrin-mode's kill-all-local-variables wiped it out.
;;
;; Earlier versions of this file mocked the `set' primitive to observe the
;; ordering.  That was fragile because `setq-local' doesn't always go through
;; the `set' function -- the byte-code path differs across Emacs versions, and
;; on Emacs master the mock missed the assignment entirely.  This rewrite uses
;; `advice-add' on `wttrin-mode' and `make-local-variable', both of which are
;; ordinary advisable functions guaranteed to be invoked on every code path
;; that defines a buffer-local binding.

;;; Code:

(require 'ert)
(require 'cl-lib)
(require 'wttrin)
(require 'testutil-wttrin)

(defvar test-wttrin--init-events nil
  "Ordered list of init events captured during the order test.
Each entry is `mode' or `xterm-color', recorded by advice on
`wttrin-mode' and `make-local-variable' respectively.  Reset before
each test run.")

(defun test-wttrin--record-mode-call (&rest _args)
  "Advice that records a `mode' event when `wttrin-mode' runs."
  (push 'mode test-wttrin--init-events))

(defun test-wttrin--record-mlv-call (symbol &rest _args)
  "Advice that records an `xterm-color' event when SYMBOL is the target.
Only triggers for `xterm-color--state' so unrelated buffer-local
declarations don't pollute the timeline."
  (when (eq symbol 'xterm-color--state)
    (push 'xterm-color test-wttrin--init-events)))

;;; Setup and Teardown

(defun test-wttrin-mode-init-setup ()
  "Setup for mode initialization tests."
  (testutil-wttrin-setup)
  (when (get-buffer "*wttr.in*")
    (kill-buffer "*wttr.in*")))

(defun test-wttrin-mode-init-teardown ()
  "Teardown for mode initialization tests."
  (testutil-wttrin-teardown)
  (when (get-buffer "*wttr.in*")
    (kill-buffer "*wttr.in*")))

;;; Tests

(ert-deftest test-wttrin-mode-initialization-order-normal-mode-runs-before-xterm-color-state-binding ()
  "Verify wttrin-mode runs before xterm-color--state is made buffer-local.

If `make-local-variable' for `xterm-color--state' ran first, the subsequent
`wttrin-mode' call would invoke `kill-all-local-variables' and wipe the
state, leaving the rendered buffer without ANSI colors.  Observing the
ordering directly catches a regression that re-introduces the bug even if
the outcome test happens to pass on a particular Emacs version."
  (test-wttrin-mode-init-setup)
  (setq test-wttrin--init-events nil)
  (advice-add 'wttrin-mode :before #'test-wttrin--record-mode-call)
  (advice-add 'make-local-variable :before #'test-wttrin--record-mlv-call)
  (unwind-protect
      (testutil-wttrin-with-clean-weather-buffer
        (wttrin--display-weather "Paris" "Test weather data")
        (let ((order (nreverse test-wttrin--init-events)))
          (should (memq 'mode order))
          (should (memq 'xterm-color order))
          (should (< (cl-position 'mode order)
                     (cl-position 'xterm-color order)))))
    (advice-remove 'wttrin-mode #'test-wttrin--record-mode-call)
    (advice-remove 'make-local-variable #'test-wttrin--record-mlv-call)
    (test-wttrin-mode-init-teardown)))

(ert-deftest test-wttrin-mode-initialization-order-normal-xterm-color-state-survives-mode-init ()
  "Test that xterm-color--state remains buffer-local after wttrin--display-weather.

This verifies that the state isn't wiped by kill-all-local-variables."
  (test-wttrin-mode-init-setup)
  (unwind-protect
      (testutil-wttrin-with-clean-weather-buffer
        (wttrin--display-weather "London" "Test data")

        (should (get-buffer "*wttr.in*"))

        (with-current-buffer "*wttr.in*"
          ;; xterm-color--state should be buffer-local
          (should (local-variable-p 'xterm-color--state))
          ;; It should have the correct value
          (should (eq xterm-color--state :char))))

    (test-wttrin-mode-init-teardown)))

(provide 'test-wttrin-mode-initialization-order)
;;; test-wttrin-mode-initialization-order.el ends here