branch: elpa/logview commit 49cca08b84a6ef26f6ce701086eaa2b6483dce8f Author: Paul Pogonyshev <pogonys...@gmail.com> Commit: Paul Pogonyshev <pogonys...@gmail.com>
Handle errors in mode initialization in such a way as to never leave it in half-initialized state (related to issue #55). --- logview.el | 13 +++++++++++++ test/logview.el | 52 +++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 56 insertions(+), 9 deletions(-) diff --git a/logview.el b/logview.el index 61ffe64430..041b2a17ff 100644 --- a/logview.el +++ b/logview.el @@ -1212,6 +1212,19 @@ successfully.") ;;;###autoload (define-derived-mode logview-mode nil "Logview" "Major mode for viewing and filtering various log files." + (let (successful) + (unwind-protect + (progn (logview--set-up) + (setf successful t)) + (unless successful + ;; Don't leave the mode half-initialized if there is any unhandled error. This is + ;; not how Emacs normally works, but standard way is too confusing to users and + ;; behavior depends on where exactly an error happened. Also, as function + ;; `normal-mode' effectively eats the error (at least its backtrace), debugging it + ;; is nearly impossible anyway. + (fundamental-mode))))) + +(defun logview--set-up () ;; {LOCKED-NARROWING} ;; Logview is incompatible with locked narrowing of Emacs 29. Later snapshots sort of ;; allow us to unlock this shit sometimes, but not the earlier, there we can only set diff --git a/test/logview.el b/test/logview.el index aca79a6b65..b4eb08dadf 100644 --- a/test/logview.el +++ b/test/logview.el @@ -22,11 +22,32 @@ (require 'subr-x) +(define-error 'logview-test-expected-error "Must be caught") + (defvar logview--test-directory (file-name-directory (or load-file-name (buffer-file-name)))) (defvar inhibit-message) +;; Copied from Eldev source code, see documentation there. +(defmacro logview--advised (spec &rest body) + (declare (indent 1) (debug (sexp body))) + (let ((symbol (nth 0 spec)) + (where (nth 1 spec)) + (function (nth 2 spec)) + (props (nthcdr 3 spec)) + (fn (make-symbol "$fn"))) + `(let ((,fn ,function)) + (when ,fn + (if (advice-member-p ,fn ,symbol) + (setf ,fn nil) + (advice-add ,symbol ,where ,fn ,@props))) + (unwind-protect + ,(macroexp-progn body) + (when ,fn + (advice-remove ,symbol ,fn)))))) + + (defmacro logview--test-with-restriction (start end locking-label &rest body) (declare (indent 3)) (if (fboundp 'with-restriction) @@ -56,8 +77,22 @@ (should (string= (buffer-string) "foo bar baz"))))))) -(defun logview--test-display-warning-advice (&rest arguments) - (error "Warning elevated to an error: %S" arguments)) +(ert-deftest logview-mode () + (with-temp-buffer + (insert "2020-01-01 00:00:00 [thread 1] INFO hello - world\n") + (logview-mode) + (should (eq major-mode 'logview-mode)))) + + +(ert-deftest logview-mode-unsuccessful-setup () + (condition-case nil + (logview--advised (#'logview--set-up :override (lambda () (signal 'logview-test-expected-error nil))) + (with-temp-buffer + (logview-mode) + ;; Errors during mode setup must not leave the mode half-initialized. + (should (eq major-mode 'fundamental-mode)))) + (logview-test-expected-error))) + (defmacro logview--test-with-file (filename &rest body) "Activate Logview in a temporary buffer with contents of the file. @@ -86,13 +121,12 @@ buffer if the test needs that." `(let (,@erase-customizations ,@extra-customizations (inhibit-message t)) - (advice-add 'display-warning :override #'logview--test-display-warning-advice) - (unwind-protect - (with-temp-buffer - (insert-file-contents (expand-file-name ,filename logview--test-directory)) - (,(or buffer-mode 'logview-mode)) - ,@body) - (advice-remove 'display-warning #'logview--test-display-warning-advice))))) + (logview--advised ('display-warning :override (lambda (&rest arguments) + (error "Warning elevated to an error: %S" arguments))) + (with-temp-buffer + (insert-file-contents (expand-file-name ,filename logview--test-directory)) + (,(or buffer-mode 'logview-mode)) + ,@body))))) (defun logview--test-user-visible-buffer-string ()