1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2025-12-08 23:40:24 -08:00

Improve ert backtrace recording

Change ert to use the new `backtrace-frames' function instead of
collecting frames one by one with `backtrace-frame'.  Additionally,
collect frames starting from `signal' instead the somewhat arbitrary
"6 from the bottom".  Skipping 6 frames would skip the expression that
actually caused the signal that triggered the debugger.  Possibly 6
was chosen because in the case of a failed test, the triggering frame
is an `ert-fail' call, which is not so interesting.  But in case of a
test throwing an error, this drops the `error' call which is too much.

* lisp/emacs-lisp/debug.el (debugger-make-xrefs): Remove.
* lisp/emacs-lisp/ert.el (ert--make-xrefs-region): Bring in relevant
code from `debugger-make-xrefs'.
(ert--print-backtrace): Add DO-XREFS parameter, delegate to
`debugger-insert-backtrace'.
(ert--run-test-debugger): Record the backtrace frames starting from
the instigating `signal' call.
(ert-run-tests-batch): Pass nil for `ert--print-backtrace's new
DO-XREFS parameter.
(ert-results-pop-to-backtrace-for-test-at-point): Pass t as DO-XREFS
to `ert--print-backtrace' and remove call to `debugger-make-xrefs'.
* test/lisp/emacs-lisp/ert-tests.el (ert-test-record-backtrace): Check
the backtrace list instead of comparing its string representation.
Expect `signal' to be the first frame.
This commit is contained in:
Noam Postavsky 2017-02-11 17:19:41 -05:00
parent 522e3c1585
commit ead545824e
3 changed files with 38 additions and 126 deletions

View file

@ -670,48 +670,12 @@ and is displayed in front of the value of MESSAGE-FORM."
(cl-defstruct (ert-test-aborted-with-non-local-exit
(:include ert-test-result)))
(defun ert--record-backtrace ()
"Record the current backtrace (as a list) and return it."
;; Since the backtrace is stored in the result object, result
;; objects must only be printed with appropriate limits
;; (`print-level' and `print-length') in place. For interactive
;; use, the cost of ensuring this possibly outweighs the advantage
;; of storing the backtrace for
;; `ert-results-pop-to-backtrace-for-test-at-point' given that we
;; already have `ert-results-rerun-test-debugging-errors-at-point'.
;; For batch use, however, printing the backtrace may be useful.
(cl-loop
;; 6 is the number of frames our own debugger adds (when
;; compiled; more when interpreted). FIXME: Need to describe a
;; procedure for determining this constant.
for i from 6
for frame = (backtrace-frame i)
while frame
collect frame))
(defun ert--print-backtrace (backtrace)
(defun ert--print-backtrace (backtrace do-xrefs)
"Format the backtrace BACKTRACE to the current buffer."
;; This is essentially a reimplementation of Fbacktrace
;; (src/eval.c), but for a saved backtrace, not the current one.
(let ((print-escape-newlines t)
(print-level 8)
(print-length 50))
(dolist (frame backtrace)
(pcase-exhaustive frame
(`(nil ,special-operator . ,arg-forms)
;; Special operator.
(insert
(format " %S\n" (cons special-operator arg-forms))))
(`(t ,fn . ,args)
;; Function call.
(insert (format " %S(" fn))
(cl-loop for firstp = t then nil
for arg in args do
(unless firstp
(insert " "))
(insert (format "%S" arg)))
(insert ")\n"))))))
(debugger-insert-backtrace backtrace do-xrefs)))
;; A container for the state of the execution of a single test and
;; environment data needed during its execution.
@ -750,7 +714,19 @@ run. ARGS are the arguments to `debugger'."
((quit) 'quit)
((ert-test-skipped) 'skipped)
(otherwise 'failed)))
(backtrace (ert--record-backtrace))
;; We store the backtrace in the result object for
;; `ert-results-pop-to-backtrace-for-test-at-point'.
;; This means we have to limit `print-level' and
;; `print-length' when printing result objects. That
;; might not be worth while when we can also use
;; `ert-results-rerun-test-debugging-errors-at-point',
;; (i.e., when running interactively) but having the
;; backtrace ready for printing is important for batch
;; use.
;;
;; Grab the frames starting from `signal', frames below
;; that are all from the debugger.
(backtrace (backtrace-frames 'signal))
(infos (reverse ert--infos)))
(setf (ert--test-execution-info-result info)
(cl-ecase type
@ -1409,8 +1385,9 @@ Returns the stats object."
(ert-test-result-with-condition
(message "Test %S backtrace:" (ert-test-name test))
(with-temp-buffer
(ert--print-backtrace (ert-test-result-with-condition-backtrace
result))
(ert--print-backtrace
(ert-test-result-with-condition-backtrace result)
nil)
(goto-char (point-min))
(while (not (eobp))
(let ((start (point))
@ -1828,12 +1805,23 @@ EWOC and STATS are arguments for `ert--results-update-stats-display'."
BEGIN and END specify a region in the current buffer."
(save-excursion
(save-restriction
(narrow-to-region begin end)
;; Inhibit optimization in `debugger-make-xrefs' that would
;; sometimes insert unrelated backtrace info into our buffer.
(let ((debugger-previous-backtrace nil))
(debugger-make-xrefs)))))
(goto-char begin)
(while (progn
(goto-char (+ (point) 2))
(skip-syntax-forward "^w_")
(< (point) end))
(let* ((beg (point))
(end (progn (skip-syntax-forward "w_") (point)))
(sym (intern-soft (buffer-substring-no-properties
beg end)))
(file (and sym (symbol-file sym 'defun))))
(when file
(goto-char beg)
;; help-xref-button needs to operate on something matched
;; by a regexp, so set that up for it.
(re-search-forward "\\(\\sw\\|\\s_\\)+")
(help-xref-button 0 'help-function-def sym file)))
(forward-line 1))))
(defun ert--string-first-line (s)
"Return the first line of S, or S if it contains no newlines.
@ -2420,8 +2408,7 @@ To be used in the ERT results buffer."
;; Use unibyte because `debugger-setup-buffer' also does so.
(set-buffer-multibyte nil)
(setq truncate-lines t)
(ert--print-backtrace backtrace)
(debugger-make-xrefs)
(ert--print-backtrace backtrace t)
(goto-char (point-min))
(insert (substitute-command-keys "Backtrace for test `"))
(ert-insert-test-name-button (ert-test-name test))