diff options
| -rw-r--r-- | tests/test-wttrin-state-file.el | 118 | ||||
| -rw-r--r-- | wttrin.el | 33 |
2 files changed, 147 insertions, 4 deletions
diff --git a/tests/test-wttrin-state-file.el b/tests/test-wttrin-state-file.el index 5648a13..34f8eb3 100644 --- a/tests/test-wttrin-state-file.el +++ b/tests/test-wttrin-state-file.el @@ -254,5 +254,123 @@ vars; only the scrub-tolerant search history stays with savehist." (should-not (memq 'wttrin--saved-locations-runtime savehist-additional-variables)))) +;;; -------------------------------------------------------------------------- +;;; Path customization after load (`:set' on `wttrin-state-file') +;;; -------------------------------------------------------------------------- + +(defmacro test-wttrin-state-file--with-path-sandbox (&rest body) + "Run BODY with a fresh temp PATH bound and the state vars isolated. +`wttrin-state-file' is let-bound to itself so a `customize-set-variable' +inside BODY is undone on exit; the `customized-value' property and the temp +file are cleaned up afterward." + (declare (indent 0)) + `(let ((path (expand-file-name + (format "wttrin-test-state-%s.el" (random 1000000)) + temporary-file-directory)) + (wttrin-state-file wttrin-state-file) + (wttrin--favorite-override nil) + (wttrin--saved-locations-runtime nil)) + (unwind-protect + (progn ,@body) + (put 'wttrin-state-file 'customized-value nil) + (when (file-exists-p path) + (delete-file path))))) + +(ert-deftest test-wttrin-state-file-normal-set-property-wired () + "Normal: `wttrin-state-file' carries the `:set' handler, so `setopt', +`customize-set-variable', and Customize all route through it." + (should (eq (get 'wttrin-state-file 'custom-set) #'wttrin--state-file-set))) + +(ert-deftest test-wttrin-state-file-normal-set-after-load-restores-from-new-path () + "Normal: customizing the path after load restores state from the new file. +This is the use-package `:config' shape — `(setopt wttrin-state-file ...)' +after `(require 'wttrin)' — where the load-time restore has already read the +default path and would otherwise never see the file the setter writes." + (test-wttrin-state-file--with-path-sandbox + (let ((wttrin-state-file path) + (wttrin--favorite-override "Salt Lake City, Utah") + (wttrin--saved-locations-runtime '(("SLC" . "40.65,-111.87")))) + (wttrin--state-save)) + (customize-set-variable 'wttrin-state-file path) + (should (equal wttrin-state-file path)) + (should (equal wttrin--favorite-override "Salt Lake City, Utah")) + (should (equal wttrin--saved-locations-runtime '(("SLC" . "40.65,-111.87")))))) + +(ert-deftest test-wttrin-state-file-normal-set-refreshes-mode-line-on-change () + "Normal: when the restore changes the effective favorite and the mode-line +mode is on, the mode-line is refreshed so it shows the restored city." + (test-wttrin-state-file--with-path-sandbox + (let ((wttrin-state-file path) + (wttrin--favorite-override "Restored, UT")) + (wttrin--state-save)) + (let ((refreshes 0) + (wttrin-favorite-location "Configured, LA") + (wttrin-mode-line-mode t)) + (cl-letf (((symbol-function 'wttrin--mode-line-refresh-now) + (lambda () (setq refreshes (1+ refreshes))))) + (customize-set-variable 'wttrin-state-file path)) + (should (= refreshes 1))))) + +(ert-deftest test-wttrin-state-file-boundary-set-same-favorite-no-refresh () + "Boundary: a restore that leaves the effective favorite unchanged does not +refresh the mode-line." + (test-wttrin-state-file--with-path-sandbox + (let ((wttrin-state-file path) + (wttrin--favorite-override "Same, UT")) + (wttrin--state-save)) + (let ((refreshes 0) + (wttrin--favorite-override "Same, UT") + (wttrin-mode-line-mode t)) + (cl-letf (((symbol-function 'wttrin--mode-line-refresh-now) + (lambda () (setq refreshes (1+ refreshes))))) + (customize-set-variable 'wttrin-state-file path)) + (should (= refreshes 0))))) + +(ert-deftest test-wttrin-state-file-boundary-set-mode-line-off-no-refresh () + "Boundary: a favorite change with the mode-line mode off does not refresh." + (test-wttrin-state-file--with-path-sandbox + (let ((wttrin-state-file path) + (wttrin--favorite-override "Restored, UT")) + (wttrin--state-save)) + (let ((refreshes 0) + (wttrin-mode-line-mode nil)) + (cl-letf (((symbol-function 'wttrin--mode-line-refresh-now) + (lambda () (setq refreshes (1+ refreshes))))) + (customize-set-variable 'wttrin-state-file path)) + (should (equal wttrin--favorite-override "Restored, UT")) + (should (= refreshes 0))))) + +(ert-deftest test-wttrin-state-file-boundary-set-absent-file-nil-vars-noop () + "Boundary: customizing to a path with no file and nil vars only sets the +path — nothing is restored and no file is created." + (test-wttrin-state-file--with-path-sandbox + (customize-set-variable 'wttrin-state-file path) + (should (equal wttrin-state-file path)) + (should-not (file-exists-p path)) + (should (null wttrin--favorite-override)))) + +(ert-deftest test-wttrin-state-file-boundary-set-absent-file-adopts-vars () + "Boundary: customizing to a path with no file while the vars hold values +adopts them into the new file, the same contract as `wttrin--state-load'." + (test-wttrin-state-file--with-path-sandbox + (setq wttrin--favorite-override "Adopted, TX") + (customize-set-variable 'wttrin-state-file path) + (should (file-exists-p path)) + (should (equal (plist-get (wttrin--state-read) :favorite-override) + "Adopted, TX")))) + +(ert-deftest test-wttrin-state-file-error-set-corrupt-file-does-not-signal () + "Error: customizing to a path holding a corrupt file sets the path, leaves +the vars untouched, and does not signal." + (test-wttrin-state-file--with-path-sandbox + (with-temp-file path (insert "(((( not lisp")) + (setq wttrin--favorite-override "Kept, OK") + (should-not + (condition-case nil + (progn (customize-set-variable 'wttrin-state-file path) nil) + (error t))) + (should (equal wttrin-state-file path)) + (should (equal wttrin--favorite-override "Kept, OK")))) + (provide 'test-wttrin-state-file) ;;; test-wttrin-state-file.el ends here @@ -666,6 +666,28 @@ would otherwise drop the entry before it could be saved." (wttrin--savehist-register) (add-hook 'savehist-save-hook #'wttrin--savehist-register)) +(defun wttrin--state-file-set (symbol value) + "Set SYMBOL (`wttrin-state-file') to VALUE and restore state from it. +The `:set' handler for the option. The load-time restore below runs against +whatever path the option holds when wttrin loads, so a path customized +afterward — the use-package `:config' shape, `(setopt wttrin-state-file ...)' +after the require — would be written by the location commands but never +read back on the next start. Re-running `wttrin--state-load' here closes +that gap for `setopt', `customize-set-variable', and Customize. When the +restore changes the effective favorite and the mode-line mode is on, the +mode-line is refreshed so it shows the restored city rather than the +configured one. At definition time the loader is not yet defined, so the +`fboundp' guard makes that first call a plain `set-default'; the load-time +restore covers the initial read." + (let ((before (and (fboundp 'wttrin--favorite-location) + (wttrin--favorite-location)))) + (set-default symbol value) + (when (fboundp 'wttrin--state-load) + (wttrin--state-load) + (when (and (not (equal before (wttrin--favorite-location))) + (bound-and-true-p wttrin-mode-line-mode)) + (wttrin--mode-line-refresh-now))))) + (defcustom wttrin-state-file (locate-user-emacs-file "wttrin-state.el") "File persisting wttrin's runtime state across sessions. Holds the runtime favorite (`wttrin--favorite-override') and the runtime @@ -675,11 +697,14 @@ geolocation commands. A dedicated file rather than savehist because savehist rewrites its whole file keeping only the variables registered in the current process: any Emacs session that saved savehist without wttrin loaded would silently delete the persisted favorite. Only wttrin writes this file. -Customize this before wttrin loads (e.g. via use-package `:custom'); the -state restores at load time, so a later `setq' takes effect only from the -next restore (`savehist-mode-hook' or restart)." +The state restores at load time from this path. Set the option with +`setopt', `customize-set-variable', or Customize (use-package `:custom' or +`:config' both work): changing it after wttrin has loaded re-reads the state +from the new path. A plain `setq' after load skips that restore and takes +effect only from the next one (`savehist-mode-hook' or restart)." :group 'wttrin - :type 'file) + :type 'file + :set #'wttrin--state-file-set) (defun wttrin--state-save () "Write the runtime favorite and directory to `wttrin-state-file'. |
