diff options
| author | Stefan Monnier | 2023-12-18 23:57:45 -0500 |
|---|---|---|
| committer | Stefan Monnier | 2024-01-04 16:33:25 -0500 |
| commit | fe0f15dbc962b37d98507a494fd7720bad584a7a (patch) | |
| tree | 6af07cc55e42b35083134019fb157b533a34b504 | |
| parent | 7959a63ce258c90eb3c7947ab3318c5531eb37d9 (diff) | |
| download | emacs-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.el | 2 | ||||
| -rw-r--r-- | lisp/emacs-lisp/ert.el | 139 |
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. | ||
| 284 | It 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 | ||
| 742 | This function records failures and errors and either terminates | 731 | This function records failures and errors and either terminates |
| 743 | the test silently or calls the interactive debugger, as | 732 | the test silently or calls the interactive debugger, as |
| 744 | appropriate. | 733 | appropriate. |
| 745 | 734 | ||
| 746 | INFO is the ert--test-execution-info corresponding to this test | 735 | INFO is the `ert--test-execution-info' corresponding to this test run. |
| 747 | run. ARGS are the arguments to `debugger'." | 736 | ERR 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 | ||
| 800 | This mainly sets up debugger-related bindings." | 782 | This 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)) |