aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Monnier2023-12-18 23:57:45 -0500
committerStefan Monnier2024-01-04 16:33:25 -0500
commitfe0f15dbc962b37d98507a494fd7720bad584a7a (patch)
tree6af07cc55e42b35083134019fb157b533a34b504
parent7959a63ce258c90eb3c7947ab3318c5531eb37d9 (diff)
downloademacs-fe0f15dbc962b37d98507a494fd7720bad584a7a.tar.gz
emacs-fe0f15dbc962b37d98507a494fd7720bad584a7a.zip
ert.el: Use `handler-bind` to record backtraces
* lisp/emacs-lisp/ert.el (ert--should-signal-hook): Delete function. (ert--expand-should-1): Don't bind `signal-hook-function`. (ert--test-execution-info): Remove `next-debugger` slot. (ert--run-test-debugger): Adjust to new calling convention. Pass the `:backtrace-base` info to the debugger. (ert--run-test-internal): Use `handler-bind` rather than let-binding `debugger` and `debug-on-error`. * lisp/emacs-lisp/ert-x.el (ert-remote-temporary-file-directory): Don't use `defconst` if it's not meant to stay constant (e.g. we let-bind it in tramp-tests.el).
-rw-r--r--lisp/emacs-lisp/ert-x.el2
-rw-r--r--lisp/emacs-lisp/ert.el139
2 files changed, 55 insertions, 86 deletions
diff --git a/lisp/emacs-lisp/ert-x.el b/lisp/emacs-lisp/ert-x.el
index 05da0f1844e..a6d2fe4a1da 100644
--- a/lisp/emacs-lisp/ert-x.el
+++ b/lisp/emacs-lisp/ert-x.el
@@ -543,7 +543,7 @@ The same keyword arguments are supported as in
543;; If this defconst is used in a test file, `tramp' shall be loaded 543;; If this defconst is used in a test file, `tramp' shall be loaded
544;; prior `ert-x'. There is no default value on w32 systems, which 544;; prior `ert-x'. There is no default value on w32 systems, which
545;; could work out of the box. 545;; could work out of the box.
546(defconst ert-remote-temporary-file-directory 546(defvar ert-remote-temporary-file-directory
547 (when (featurep 'tramp) 547 (when (featurep 'tramp)
548 (cond 548 (cond
549 ((getenv "REMOTE_TEMPORARY_FILE_DIRECTORY")) 549 ((getenv "REMOTE_TEMPORARY_FILE_DIRECTORY"))
diff --git a/lisp/emacs-lisp/ert.el b/lisp/emacs-lisp/ert.el
index 353c1bd09d2..8ab57d2b238 100644
--- a/lisp/emacs-lisp/ert.el
+++ b/lisp/emacs-lisp/ert.el
@@ -278,14 +278,6 @@ DATA is displayed to the user and should state the reason for skipping."
278 (when ert--should-execution-observer 278 (when ert--should-execution-observer
279 (funcall ert--should-execution-observer form-description))) 279 (funcall ert--should-execution-observer form-description)))
280 280
281;; See Bug#24402 for why this exists
282(defun ert--should-signal-hook (error-symbol data)
283 "Stupid hack to stop `condition-case' from catching ert signals.
284It should only be stopped when ran from inside `ert--run-test-internal'."
285 (when (and (not (symbolp debugger)) ; only run on anonymous debugger
286 (memq error-symbol '(ert-test-failed ert-test-skipped)))
287 (funcall debugger 'error (cons error-symbol data))))
288
289(defun ert--special-operator-p (thing) 281(defun ert--special-operator-p (thing)
290 "Return non-nil if THING is a symbol naming a special operator." 282 "Return non-nil if THING is a symbol naming a special operator."
291 (and (symbolp thing) 283 (and (symbolp thing)
@@ -324,8 +316,7 @@ It should only be stopped when ran from inside `ert--run-test-internal'."
324 (default-value (gensym "ert-form-evaluation-aborted-"))) 316 (default-value (gensym "ert-form-evaluation-aborted-")))
325 `(let* ((,fn (function ,fn-name)) 317 `(let* ((,fn (function ,fn-name))
326 (,args (condition-case err 318 (,args (condition-case err
327 (let ((signal-hook-function #'ert--should-signal-hook)) 319 (list ,@arg-forms)
328 (list ,@arg-forms))
329 (error (progn (setq ,fn #'signal) 320 (error (progn (setq ,fn #'signal)
330 (list (car err) 321 (list (car err)
331 (cdr err))))))) 322 (cdr err)))))))
@@ -728,78 +719,68 @@ in front of the value of MESSAGE-FORM."
728 ;; value and test execution should be terminated. Should not 719 ;; value and test execution should be terminated. Should not
729 ;; return. 720 ;; return.
730 (exit-continuation (cl-assert nil)) 721 (exit-continuation (cl-assert nil))
731 ;; The binding of `debugger' outside of the execution of the test.
732 next-debugger
733 ;; The binding of `ert-debug-on-error' that is in effect for the 722 ;; The binding of `ert-debug-on-error' that is in effect for the
734 ;; execution of the current test. We store it to avoid being 723 ;; execution of the current test. We store it to avoid being
735 ;; affected by any new bindings the test itself may establish. (I 724 ;; affected by any new bindings the test itself may establish. (I
736 ;; don't remember whether this feature is important.) 725 ;; don't remember whether this feature is important.)
737 ert-debug-on-error) 726 ert-debug-on-error)
738 727
739(defun ert--run-test-debugger (info args) 728(defun ert--run-test-debugger (info condition debugfun)
740 "During a test run, `debugger' is bound to a closure that calls this function. 729 "Error handler used during the test run.
741 730
742This function records failures and errors and either terminates 731This function records failures and errors and either terminates
743the test silently or calls the interactive debugger, as 732the test silently or calls the interactive debugger, as
744appropriate. 733appropriate.
745 734
746INFO is the ert--test-execution-info corresponding to this test 735INFO is the `ert--test-execution-info' corresponding to this test run.
747run. ARGS are the arguments to `debugger'." 736ERR is the error object."
748 (cl-destructuring-bind (first-debugger-arg &rest more-debugger-args) 737 (let* ((type (cl-case (car condition)
749 args 738 ((quit) 'quit)
750 (cl-ecase first-debugger-arg 739 ((ert-test-skipped) 'skipped)
751 ((lambda debug t exit nil) 740 (otherwise 'failed)))
752 (apply (ert--test-execution-info-next-debugger info) args)) 741 ;; We store the backtrace in the result object for
753 (error 742 ;; `ert-results-pop-to-backtrace-for-test-at-point'.
754 (let* ((condition (car more-debugger-args)) 743 ;; This means we have to limit `print-level' and
755 (type (cl-case (car condition) 744 ;; `print-length' when printing result objects. That
756 ((quit) 'quit) 745 ;; might not be worth while when we can also use
757 ((ert-test-skipped) 'skipped) 746 ;; `ert-results-rerun-test-at-point-debugging-errors',
758 (otherwise 'failed))) 747 ;; (i.e., when running interactively) but having the
759 ;; We store the backtrace in the result object for 748 ;; backtrace ready for printing is important for batch
760 ;; `ert-results-pop-to-backtrace-for-test-at-point'. 749 ;; use.
761 ;; This means we have to limit `print-level' and 750 ;;
762 ;; `print-length' when printing result objects. That 751 ;; Grab the frames above ourselves.
763 ;; might not be worth while when we can also use 752 (backtrace (cdr (backtrace-get-frames debugfun)))
764 ;; `ert-results-rerun-test-at-point-debugging-errors', 753 (infos (reverse ert--infos)))
765 ;; (i.e., when running interactively) but having the 754 (setf (ert--test-execution-info-result info)
766 ;; backtrace ready for printing is important for batch 755 (cl-ecase type
767 ;; use. 756 (quit
768 ;; 757 (make-ert-test-quit :condition condition
769 ;; Grab the frames above the debugger. 758 :backtrace backtrace
770 (backtrace (cdr (backtrace-get-frames debugger))) 759 :infos infos))
771 (infos (reverse ert--infos))) 760 (skipped
772 (setf (ert--test-execution-info-result info) 761 (make-ert-test-skipped :condition condition
773 (cl-ecase type 762 :backtrace backtrace
774 (quit 763 :infos infos))
775 (make-ert-test-quit :condition condition 764 (failed
776 :backtrace backtrace 765 (make-ert-test-failed :condition condition
777 :infos infos)) 766 :backtrace backtrace
778 (skipped 767 :infos infos))))
779 (make-ert-test-skipped :condition condition 768 ;; FIXME: We should probably implement more fine-grained
780 :backtrace backtrace 769 ;; control a la non-t `debug-on-error' here.
781 :infos infos)) 770 (cond
782 (failed 771 ((ert--test-execution-info-ert-debug-on-error info)
783 (make-ert-test-failed :condition condition 772 ;; The `debugfun' arg tells `debug' which backtrace frame starts
784 :backtrace backtrace 773 ;; the "entering the debugger" code so it can hide those frames
785 :infos infos)))) 774 ;; from the backtrace.
786 ;; Work around Emacs's heuristic (in eval.c) for detecting 775 (funcall debugger 'error condition :backtrace-base debugfun))
787 ;; errors in the debugger. 776 (t))
788 (cl-incf num-nonmacro-input-events) 777 (funcall (ert--test-execution-info-exit-continuation info))))
789 ;; FIXME: We should probably implement more fine-grained
790 ;; control a la non-t `debug-on-error' here.
791 (cond
792 ((ert--test-execution-info-ert-debug-on-error info)
793 (apply (ert--test-execution-info-next-debugger info) args))
794 (t))
795 (funcall (ert--test-execution-info-exit-continuation info)))))))
796 778
797(defun ert--run-test-internal (test-execution-info) 779(defun ert--run-test-internal (test-execution-info)
798 "Low-level function to run a test according to TEST-EXECUTION-INFO. 780 "Low-level function to run a test according to TEST-EXECUTION-INFO.
799 781
800This mainly sets up debugger-related bindings." 782This mainly sets up debugger-related bindings."
801 (setf (ert--test-execution-info-next-debugger test-execution-info) debugger 783 (setf (ert--test-execution-info-ert-debug-on-error test-execution-info)
802 (ert--test-execution-info-ert-debug-on-error test-execution-info)
803 ert-debug-on-error) 784 ert-debug-on-error)
804 (catch 'ert--pass 785 (catch 'ert--pass
805 ;; For now, each test gets its own temp buffer and its own 786 ;; For now, each test gets its own temp buffer and its own
@@ -807,26 +788,14 @@ This mainly sets up debugger-related bindings."
807 ;; too expensive, we can remove it. 788 ;; too expensive, we can remove it.
808 (with-temp-buffer 789 (with-temp-buffer
809 (save-window-excursion 790 (save-window-excursion
810 ;; FIXME: Use `signal-hook-function' instead of `debugger' to 791 (let ((lexical-binding t) ;;FIXME: Why?
811 ;; handle ert errors. Once that's done, remove
812 ;; `ert--should-signal-hook'. See Bug#24402 and Bug#11218 for
813 ;; details.
814 (let ((lexical-binding t)
815 (debugger (lambda (&rest args)
816 (ert--run-test-debugger test-execution-info
817 args)))
818 (debug-on-error t)
819 ;; Don't infloop if the error being called is erroring
820 ;; out, and we have `debug-on-error' bound to nil inside
821 ;; the test.
822 (backtrace-on-error-noninteractive nil)
823 (debug-on-quit t)
824 ;; FIXME: Do we need to store the old binding of this
825 ;; and consider it in `ert--run-test-debugger'?
826 (debug-ignored-errors nil)
827 (ert--infos '())) 792 (ert--infos '()))
828 (funcall (ert-test-body (ert--test-execution-info-test 793 (letrec ((debugfun (lambda (err)
829 test-execution-info)))))) 794 (ert--run-test-debugger test-execution-info
795 err debugfun))))
796 (handler-bind (((error quit) debugfun))
797 (funcall (ert-test-body (ert--test-execution-info-test
798 test-execution-info))))))))
830 (ert-pass)) 799 (ert-pass))
831 (setf (ert--test-execution-info-result test-execution-info) 800 (setf (ert--test-execution-info-result test-execution-info)
832 (make-ert-test-passed)) 801 (make-ert-test-passed))