From 7b8322f6285702faf5da0824b9b195619da9c698 Mon Sep 17 00:00:00 2001 From: "F. Jason Park" Date: Mon, 16 Jan 2023 23:05:16 -0800 Subject: [PATCH] Use correct buffer for local-module vars in erc-open * lisp/erc/erc.el (erc--target-priors): New internal variable to do for target buffers what `erc--server-reconnecting' does for server buffers. (erc-open): Source the state of a local module's mode variable from its actual buffer rather than its server buffer. Additionally, make all local variables from a prior session available to module-activation functions and `erc-mode' hooks, even when `erc-reuse-buffers' is nil. This bug arrived with the introduction of "local-modules" (bug#57955). * test/lisp/erc/erc-scenarios-base-local-modules.el (erc-scenarios-base-local-modules--toggle-helpers): Remove useless `with-current-buffer'. (erc-scenarios-base-local-modules--local-var, erc--phony-sblm--enable, erc--phony-sblm--disable, erc--phony-sblm--mode): Add fake local module and data var for test scenario. (erc-scenarios-base-local-modules--var-persistence) Add slightly hacky test case with promise to improve later when splitting the file. --- lisp/erc/erc.el | 15 ++- .../erc/erc-scenarios-base-local-modules.el | 100 ++++++++++++++++-- 2 files changed, 106 insertions(+), 9 deletions(-) diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el index ba7db15cf8c..e7f81f24ac4 100644 --- a/lisp/erc/erc.el +++ b/lisp/erc/erc.el @@ -1305,6 +1305,14 @@ See also `erc-show-my-nick'." (defvar-local erc-dbuf nil) +;; See comments in `erc-scenarios-base-local-modules' explaining why +;; this is insufficient as a public interface. + +(defvar erc--target-priors nil + "Analogous to `erc--server-reconnecting' but for target buffers. +Bound to local variables from an existing (logical) session's +buffer during local-module setup and `erc-mode-hook' activation.") + (defun erc--target-from-string (string) "Construct an `erc--target' variant from STRING." (funcall (if (erc-channel-p string) @@ -1985,7 +1993,9 @@ Returns the buffer for the given server or channel." (let* ((target (and channel (erc--target-from-string channel))) (buffer (erc-get-buffer-create server port nil target id)) (old-buffer (current-buffer)) - (old-vars (and target (buffer-local-variables))) + (erc--target-priors (and target ; buf from prior session + (buffer-local-value 'erc--target buffer) + (buffer-local-variables buffer))) (old-recon-count erc-server-reconnect-count) (old-point nil) (delayed-modules nil) @@ -1998,7 +2008,8 @@ Returns the buffer for the given server or channel." (setq old-point (point)) (setq delayed-modules (erc--merge-local-modes (erc--update-modules) - (or erc--server-reconnecting old-vars))) + (or erc--server-reconnecting + erc--target-priors))) (delay-mode-hooks (erc-mode)) diff --git a/test/lisp/erc/erc-scenarios-base-local-modules.el b/test/lisp/erc/erc-scenarios-base-local-modules.el index d4001df45de..916d105779a 100644 --- a/test/lisp/erc/erc-scenarios-base-local-modules.el +++ b/test/lisp/erc/erc-scenarios-base-local-modules.el @@ -19,8 +19,17 @@ ;;; Commentary: -;; These tests all use `sasl' because, as of ERC 5.5, it's the one -;; and only local module. +;; A local module doubles as a minor mode whose mode variable and +;; associated local data can withstand service disruptions. +;; Unfortunately, the current implementation is too unwieldy to be +;; made public because it doesn't perform any of the boiler plate +;; needed to save and restore buffer-local and "network-local" copies +;; of user options. Ultimately, a user-friendly framework must fill +;; this void if third-party local modules are ever to become +;; practical. +;; +;; The following tests all use `sasl' because, as of ERC 5.5, it's the +;; only local module. ;;; Code: @@ -206,7 +215,7 @@ (erc-cmd-QUIT "") (funcall expect 10 "finished"))) - (ert-info ("Disabling works from a target buffer.") + (ert-info ("Disabling works from a target buffer") (with-current-buffer "#chan" (should erc-sasl-mode) (call-interactively #'erc-sasl-disable) @@ -214,10 +223,9 @@ (should (local-variable-p 'erc-sasl-mode)) (should-not (buffer-local-value 'erc-sasl-mode (get-buffer "foonet"))) (erc-cmd-RECONNECT) - (with-current-buffer "#chan" - (funcall expect 10 "Some enigma, some riddle") - (should-not erc-sasl-mode) ; regression - (should (local-variable-p 'erc-sasl-mode)))) + (funcall expect 10 "Some enigma, some riddle") + (should-not erc-sasl-mode) ; regression + (should (local-variable-p 'erc-sasl-mode))) (with-current-buffer "foonet" (should (local-variable-p 'erc-sasl-mode)) @@ -239,4 +247,82 @@ (should erc-sasl-mode) (funcall expect 10 "User modes for tester"))))) +(defvar-local erc-scenarios-base-local-modules--local-var nil) + +(define-erc-module -phony-sblm- nil + "Test module for `erc-scenarios-base-local-modules--var-persistence'." + ((when-let ((vars (or erc--server-reconnecting erc--target-priors))) + (should (assq 'erc--phony-sblm--mode vars)) + (setq erc-scenarios-base-local-modules--local-var + (alist-get 'erc-scenarios-base-local-modules--local-var vars))) + (setq erc-scenarios-base-local-modules--local-var + (or erc-scenarios-base-local-modules--local-var + (if erc--target 100 0)))) + ((kill-local-variable 'erc-scenarios-base-local-modules--local-var)) + 'local) + +;; Note: this file has grown too expensive (time-wise) and must be +;; split up. When that happens, this test should be rewritten without +;; any time-saving hacks, namely, server-initiated JOINs and an +;; absence of QUITs. (That said, three connections in under 2 seconds +;; is pretty nice.) + +(ert-deftest erc-scenarios-base-local-modules--var-persistence () + :tags '(:expensive-test) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "base/reconnect") + (erc-server-flood-penalty 0.1) + (dumb-server (erc-d-run "localhost" t 'options 'options 'options)) + (port (process-contact dumb-server :service)) + (erc-modules (cons '-phony-sblm- (remq 'autojoin erc-modules))) + (expect (erc-d-t-make-expecter)) + (server-buffer-name (format "127.0.0.1:%d" port))) + + (ert-info ("Initial authentication succeeds as expected") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :nick "tester" + :password "changeme" + :full-name "tester") + (should (string= (buffer-name) server-buffer-name))) + + (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "FooNet")) + (funcall expect 10 "This server is in debug mode") + (should erc--phony-sblm--mode) + (should (eql erc-scenarios-base-local-modules--local-var 0)) + (setq erc-scenarios-base-local-modules--local-var 1))) + + (ert-info ("Save module's local var in target buffer") + (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan")) + (should (eql erc-scenarios-base-local-modules--local-var 100)) + (setq erc-scenarios-base-local-modules--local-var 101) + (funcall expect 20 "welcome"))) + + (with-current-buffer "FooNet" (funcall expect 20 "terminated")) + + (ert-info ("Vars reused when mode was left enabled") + (with-current-buffer "#chan" + (erc-cmd-RECONNECT) + (funcall expect 20 "welcome") + (should (eql erc-scenarios-base-local-modules--local-var 101)) + (erc--phony-sblm--mode -1)) + + (with-current-buffer "FooNet" + (funcall expect 10 "User modes for tester") + (should (eql erc-scenarios-base-local-modules--local-var 1)))) + + (with-current-buffer "FooNet" (funcall expect 20 "terminated")) + + (ert-info ("Local binding gone when mode disabled in target") + (with-current-buffer "#chan" + (erc-cmd-RECONNECT) + (funcall expect 20 "welcome") + (should-not erc--phony-sblm--mode) + (should-not erc-scenarios-base-local-modules--local-var)) + + ;; But value retained in server buffer, where mode is active. + (with-current-buffer "FooNet" + (funcall expect 10 "User modes for tester") + (should (eql erc-scenarios-base-local-modules--local-var 1)))))) + ;;; erc-scenarios-local-modules.el ends here -- 2.39.2