Commit e09120d6 authored by Gemini Lasswell's avatar Gemini Lasswell

Add backtrace-mode and use it in the debugger, ERT and Edebug

* doc/lispref/debugging.texi (Using Debugger): Remove explanation of
backtrace buffer.  Refer to new node.
(Backtraces): New node.
(Debugger Commands): Refer to new node.  Remove 'v'.
* doc/lispref/edebug.texi (Edebug Misc): Refer to new node.
* doc/misc/ert.texi (Running Tests Interactively): Refer to new node.

* lisp/emacs-lisp-backtrace.el: New file.
* test/lisp/emacs-lisp/backtrace-tests.el: New file.

* lisp/emacs-lisp/debug.el: (debugger-buffer-state): New cl-defstruct.
(debugger--restore-buffer-state): New function.
(debug): Use a debugger-buffer-state object to save and restore buffer
state.  Fix bug#15749 by leaving an unused buffer in debugger-mode,
empty, instead of in fundamental-mode, and then when reusing a buffer,
not calling debugger-mode if the buffer is already in debugger-mode.
(debugger-insert-backtrace): Remove.
(debugger-setup-buffer): Use backtrace-mode.
(debugger--insert-header): New function.
(debugger-continue, debugger-return-value): Change check for flags to
use backtrace-frames.
(debugger-frame-number): Determine backtrace frame number from
backtrace-frames.
(debugger--locals-visible-p, debugger--insert-locals)
(debugger--show-locals, debugger--hide-locals)
(debugger-toggle-locals): Remove.
(debugger-mode-map): Make a child of backtrace-mode-map.  Move
navigation commands to backtrace-mode-map.  Bind 'q' to debugger-quit
instead of top-level.  Make Help Follow menu item call
backtrace-help-follow-symbol.
(debugger-mode): Derive from backtrace-mode.
(debug-help-follow): Remove.  Move body of this function to
'backtrace-help-follow-symbol' in backtrace.el.
(debugger-quit): New function.

* lisp/emacs-lisp/edebug.el (edebug-unwrap-results): Remove warning
in docstring about circular results.
(edebug-unwrap): Use pcase.
(edebug-unwrap1): New function to unwrap circular objects.
(edebug-unwrap*): Use it.
(edebug--frame): New cl-defstruct.
(edebug-backtrace): Call the buffer *Edebug Backtrace* and use
backtrace-mode.  Get the frames from edebug--backtrace-frames.
(edebug--backtrace-frames, edebug--unwrap-and-add-info)
(edebug--symbol-not-prefixed-p): New functions.

* lisp/emacs-lisp/lisp-mode.el
(lisp-el-font-lock-keywords-for-backtraces)
(lisp-el-font-lock-keywords-for-backtraces-1)
(lisp-el-font-lock-keywords-for-backtraces-2): New constants.

* lisp/emacs-lisp/ert.el (ert--print-backtrace): Remove.
(ert--run-test-debugger): Use backtrace-get-frames.
(ert-run-tests-batch): Use backtrace-to-string.
(ert-results-pop-to-backtrace-for-test-at-point): Use backtrace-mode.
(ert--insert-backtrace-header): New function.

* tests/lisp/emacs-lisp/ert-tests.el (ert-test--which-file):
Use backtrace-frame slot accessor.
parent 8a762095
......@@ -81,7 +81,8 @@ debugger recursively. @xref{Recursive Editing}.
* Function Debugging:: Entering it when a certain function is called.
* Variable Debugging:: Entering it when a variable is modified.
* Explicit Debug:: Entering it at a certain point in the program.
* Using Debugger:: What the debugger does; what you see while in it.
* Using Debugger:: What the debugger does.
* Backtraces:: What you see while in the debugger.
* Debugger Commands:: Commands used while in the debugger.
* Invoking the Debugger:: How to call the function @code{debug}.
* Internals of Debugger:: Subroutines of the debugger, and global variables.
......@@ -392,32 +393,79 @@ this is not what you want, you can either set
@code{eval-expression-debug-on-error} to @code{nil}, or set
@code{debug-on-error} to @code{nil} in @code{debugger-mode-hook}.
The debugger itself must be run byte-compiled, since it makes
assumptions about the state of the Lisp interpreter. These
assumptions are false if the debugger is running interpreted.
@node Backtraces
@subsection Backtraces
@cindex backtrace buffer
Debugger mode is derived from Backtrace mode, which is also used to
show backtraces by Edebug and ERT. (@pxref{Edebug} and @ref{Top,the
ERT manual,, ert, ERT: Emacs Lisp Regression Testing})
@cindex stack frame
The backtrace buffer shows you the functions that are executing and
their argument values. When a backtrace buffer is created, it shows
each stack frame on one, possibly very long, line. (A stack frame is
the place where the Lisp interpreter records information about a
particular invocation of a function.) The most recently called
function will be at the top.
@cindex current stack frame
The backtrace buffer shows you the functions that are executing and
their argument values. It also allows you to specify a stack frame by
moving point to the line describing that frame. (A stack frame is the
place where the Lisp interpreter records information about a particular
invocation of a function.) The frame whose line point is on is
considered the @dfn{current frame}. Some of the debugger commands
operate on the current frame. If a line starts with a star, that means
that exiting that frame will call the debugger again. This is useful
for examining the return value of a function.
If a function name is underlined, that means the debugger knows
where its source code is located. You can click with the mouse on
that name, or move to it and type @key{RET}, to visit the source code.
In a backtrace you can specify a stack frame by moving point to a line
describing that frame. The frame whose line point is on is considered
the @dfn{current frame}.
If a function name is underlined, that means Emacs knows where its
source code is located. You can click with the mouse on that name, or
move to it and type @key{RET}, to visit the source code. You can also
type @key{RET} while point is on any name of a function or variable
which is not underlined, to see help information for that symbol in a
help buffer, if any exists. The @code{xref-find-definitions} command,
bound to @key{M-.}, can also be used on any identifier in a backtrace
(@pxref{Looking Up Identifiers,,,emacs,Emacs manual}).
In backtraces, the tails of long lists and the ends of long strings,
vectors or structures, as well as objects which are deeply nested,
will be printed as underlined ``...''. You can click with the mouse
on a ``...'', or type @key{RET} while point is on it, to show the part
of the object that was hidden. To control how much abbreviation is
done, customize @code{backtrace-line-length}.
Here is a list of commands for navigating and viewing backtraces:
The debugger itself must be run byte-compiled, since it makes
assumptions about how many stack frames are used for the debugger
itself. These assumptions are false if the debugger is running
interpreted.
@table @kbd
@item v
Toggle the display of local variables of the current stack frame.
@item p
Move to the beginning of the frame, or to the beginning
of the previous frame.
@item n
Move to the beginning of the next frame.
@item +
Add line breaks and indentation to the top-level Lisp form at point to
make it more readable.
@item =
Collapse the top-level Lisp form at point back to a single line.
@item #
Toggle @code{print-circle} for the frame at point.
@end table
@node Debugger Commands
@subsection Debugger Commands
@cindex debugger command list
The debugger buffer (in Debugger mode) provides special commands in
addition to the usual Emacs commands. The most important use of
addition to the usual Emacs commands and to the Backtrace mode commands
described in the previous section. The most important use of
debugger commands is for stepping through code, so that you can see
how control flows. The debugger can step through the control
structures of an interpreted function, but cannot do so in a
......@@ -427,6 +475,11 @@ the same function. (To do this, visit the source for the function and
type @kbd{C-M-x} on its definition.) You cannot use the Lisp debugger
to step through a primitive function.
Some of the debugger commands operate on the current frame. If a
frame starts with a star, that means that exiting that frame will call the
debugger again. This is useful for examining the return value of a
function.
@c FIXME: Add @findex for the following commands? --xfq
Here is a list of Debugger mode commands:
......@@ -502,8 +555,6 @@ Display a list of functions that will invoke the debugger when called.
This is a list of functions that are set to break on entry by means of
@code{debug-on-entry}.
@item v
Toggle the display of local variables of the current stack frame.
@end table
@node Invoking the Debugger
......
......@@ -442,8 +442,8 @@ Redisplay the most recently known expression result in the echo area
Display a backtrace, excluding Edebug's own functions for clarity
(@code{edebug-backtrace}).
You cannot use debugger commands in the backtrace buffer in Edebug as
you would in the standard debugger.
@xref{Debugging,, Backtraces, elisp}, for the commands which work
in a backtrace buffer.
The backtrace buffer is killed automatically when you continue
execution.
......
......@@ -273,9 +273,11 @@ moving point to it and typing @kbd{@key{RET}} jumps to its definition.
@cindex backtrace of a failed test
Pressing @kbd{r} re-runs the test near point on its own. Pressing
@kbd{d} re-runs it with the debugger enabled. @kbd{.} jumps to the
definition of the test near point (@kbd{@key{RET}} has the same effect if
point is on the name of the test). On a failed test, @kbd{b} shows
the backtrace of the failure.
definition of the test near point (@kbd{@key{RET}} has the same effect
if point is on the name of the test). On a failed test, @kbd{b} shows
the backtrace of the failure. @xref{Debugging,, Backtraces, elisp,
the Emacs Lisp Reference Manual}, for more information about
backtraces.
@kindex l@r{, in ert results buffer}
@kbd{l} shows the list of @code{should} forms executed in the test.
......
......@@ -466,6 +466,14 @@ the shift key.
*** Isearch now remembers the regexp-based search mode for words/symbols
and case-sensitivity together with search strings in the search ring.
** Debugger
+++
*** The Lisp Debugger is now based on 'backtrace-mode'.
Backtrace mode adds fontification and commands for changing the
appearance of backtrace frames. See the node "Backtraces" in the Elisp
manual for documentation of the new mode and its commands.
** Edebug
+++
......@@ -475,14 +483,18 @@ using the new variables 'edebug-behavior-alist',
'edebug-new-definition-function'. Edebug's behavior can be changed
globally or for individual definitions.
+++
*** Edebug's backtrace buffer now uses 'backtrace-mode'.
Backtrace mode adds fontification, links and commands for changing the
appearance of backtrace frames. See the node "Backtraces" in the Elisp
manual for documentation of the new mode and its commands.
** Enhanced xterm support
*** New variable 'xterm-set-window-title' controls whether Emacs sets
the XTerm window title. This feature is experimental and is disabled
by default.
** Gamegrid
** grep
+++
......@@ -499,6 +511,14 @@ The abbreviation can be disabled by the new option
*** New variable 'ert-quiet' allows to make ERT output in batch mode
less verbose by removing non-essential information.
+++
*** ERT's backtrace buffer now uses 'backtrace-mode'.
Backtrace mode adds fontification and commands for changing the
appearance of backtrace frames. See the node "Backtraces" in the Elisp
manual for documentation of the new mode and its commands.
** Gamegrid
---
*** Gamegrid now determines its default glyph size based on display
dimensions, instead of always using 16 pixels. As a result, Tetris,
......@@ -669,6 +689,13 @@ transport strategies as well as a separate API to use them. A
transport implementation for process-based communication, such as is
used by the Language Server Protocol (LSP), is readily available.
+++
** Backtrace mode improves viewing of Elisp backtraces.
Backtrace mode adds pretty printing, fontification and ellipsis
expansion to backtrace buffers produced by the Lisp debugger, Edebug
and ERT. See the node "Backtraces" in the Elisp manual for
documentation of the new mode and its commands.
* Incompatible Lisp Changes in Emacs 27.1
......
This diff is collapsed.
This diff is collapsed.
......@@ -52,6 +52,7 @@
;;; Code:
(require 'backtrace)
(require 'macroexp)
(require 'cl-lib)
(eval-when-compile (require 'pcase))
......@@ -206,8 +207,7 @@ Use this with caution since it is not debugged."
"Non-nil if Edebug should unwrap results of expressions.
That is, Edebug will try to remove its own instrumentation from the result.
This is useful when debugging macros where the results of expressions
are instrumented expressions. But don't do this when results might be
circular or an infinite loop will result."
are instrumented expressions."
:type 'boolean
:group 'edebug)
......@@ -1265,25 +1265,59 @@ purpose by adding an entry to this alist, and setting
(defun edebug-unwrap (sexp)
"Return the unwrapped SEXP or return it as is if it is not wrapped.
The SEXP might be the result of wrapping a body, which is a list of
expressions; a `progn' form will be returned enclosing these forms."
(if (consp sexp)
(cond
((eq 'edebug-after (car sexp))
(nth 3 sexp))
((eq 'edebug-enter (car sexp))
(macroexp-progn (nthcdr 2 (nth 1 (nth 3 sexp)))))
(t sexp);; otherwise it is not wrapped, so just return it.
)
sexp))
expressions; a `progn' form will be returned enclosing these forms.
Does not unwrap inside vectors, records, structures, or hash tables."
(pcase sexp
(`(edebug-after ,_before-form ,_after-index ,form)
form)
(`(lambda ,args (edebug-enter ',_sym ,_arglist
(function (lambda nil . ,body))))
`(lambda ,args ,@body))
(`(closure ,env ,args (edebug-enter ',_sym ,_arglist
(function (lambda nil . ,body))))
`(closure ,env ,args ,@body))
(`(edebug-enter ',_sym ,_args (function (lambda nil . ,body)))
(macroexp-progn body))
(_ sexp)))
(defun edebug-unwrap* (sexp)
"Return the SEXP recursively unwrapped."
(let ((ht (make-hash-table :test 'eq)))
(edebug--unwrap1 sexp ht)))
(defun edebug--unwrap1 (sexp hash-table)
"Unwrap SEXP using HASH-TABLE of things already unwrapped.
HASH-TABLE contains the results of unwrapping cons cells within
SEXP, which are reused to avoid infinite loops when SEXP is or
contains a circular object."
(let ((new-sexp (edebug-unwrap sexp)))
(while (not (eq sexp new-sexp))
(setq sexp new-sexp
new-sexp (edebug-unwrap sexp)))
(if (consp new-sexp)
(mapcar #'edebug-unwrap* new-sexp)
(let ((result (gethash new-sexp hash-table nil)))
(unless result
(let ((remainder new-sexp)
current)
(setq result (cons nil nil)
current result)
(while
(progn
(puthash remainder current hash-table)
(setf (car current)
(edebug--unwrap1 (car remainder) hash-table))
(setq remainder (cdr remainder))
(cond
((atom remainder)
(setf (cdr current)
(edebug--unwrap1 remainder hash-table))
nil)
((gethash remainder hash-table nil)
(setf (cdr current) (gethash remainder hash-table nil))
nil)
(t (setq current
(setf (cdr current) (cons nil nil)))))))))
result)
new-sexp)))
......@@ -3916,8 +3950,10 @@ Global commands prefixed by `global-edebug-prefix':
;; (setq debugger 'debug) ; use the standard debugger
;; Note that debug and its utilities must be byte-compiled to work,
;; since they depend on the backtrace looking a certain way. But
;; edebug is not dependent on this, yet.
;; since they depend on the backtrace looking a certain way. Edebug
;; will work if not byte-compiled, but it will not be able correctly
;; remove its instrumentation from backtraces unless it is
;; byte-compiled.
(defun edebug (&optional arg-mode &rest args)
"Replacement for `debug'.
......@@ -3947,48 +3983,96 @@ Otherwise call `debug' normally."
(apply #'debug arg-mode args)
))
;;; Backtrace buffer
;; Data structure for backtrace frames with information
;; from Edebug instrumentation found in the backtrace.
(cl-defstruct
(edebug--frame
(:constructor edebug--make-frame)
(:include backtrace-frame))
def-name before-index after-index)
(defun edebug-backtrace ()
"Display a non-working backtrace. Better than nothing..."
"Display the current backtrace in a `backtrace-mode' window."
(interactive)
(if (or (not edebug-backtrace-buffer)
(null (buffer-name edebug-backtrace-buffer)))
(setq edebug-backtrace-buffer
(generate-new-buffer "*Backtrace*"))
(generate-new-buffer "*Edebug Backtrace*"))
;; Else, could just display edebug-backtrace-buffer.
)
(with-output-to-temp-buffer (buffer-name edebug-backtrace-buffer)
(setq edebug-backtrace-buffer standard-output)
(let ((print-escape-newlines t)
(print-length 50) ; FIXME cf edebug-safe-prin1-to-string
last-ok-point)
(backtrace)
;; Clean up the backtrace.
;; Not quite right for current edebug scheme.
(set-buffer edebug-backtrace-buffer)
(setq truncate-lines t)
(goto-char (point-min))
(setq last-ok-point (point))
(if t (progn
;; Delete interspersed edebug internals.
(while (re-search-forward "^ (?edebug" nil t)
(beginning-of-line)
(cond
((looking-at "^ (edebug-after")
;; Previous lines may contain code, so just delete this line.
(setq last-ok-point (point))
(forward-line 1)
(delete-region last-ok-point (point)))
((looking-at (if debugger-stack-frame-as-list
"^ (edebug"
"^ edebug"))
(forward-line 1)
(delete-region last-ok-point (point))
)))
)))))
(with-current-buffer edebug-backtrace-buffer
(unless (derived-mode-p 'backtrace-mode)
(backtrace-mode))
(setq backtrace-frames (edebug--backtrace-frames)
backtrace-view '(:do-xrefs t))
(backtrace-print)
(goto-char (point-min)))))
(defun edebug--backtrace-frames ()
"Return backtrace frames with instrumentation removed.
Remove frames for Edebug's functions and the lambdas in
`edebug-enter' wrappers."
(let* ((frames (backtrace-get-frames 'edebug-debugger
:constructor #'edebug--make-frame))
skip-next-lambda def-name before-index after-index
results
(index (length frames)))
(dolist (frame (reverse frames))
(let ((fun (edebug--frame-fun frame))
(args (edebug--frame-args frame)))
(cl-decf index)
(when (edebug--frame-evald frame)
(setq before-index nil
after-index nil))
(pcase fun
('edebug-enter
(setq skip-next-lambda t
def-name (nth 0 args)))
('edebug-after
(setq before-index (if (consp (nth 0 args))
(nth 1 (nth 0 args))
(nth 0 args))
after-index (nth 1 args)))
((pred edebug--symbol-not-prefixed-p)
(edebug--unwrap-and-add-info frame def-name before-index after-index)
(setf (edebug--frame-def-name frame) (and before-index def-name))
(setf (edebug--frame-before-index frame) before-index)
(setf (edebug--frame-after-index frame) after-index)
(push frame results)
(setq before-index nil
after-index nil))
(`(,(or 'lambda 'closure) . ,_)
(unless skip-next-lambda
(edebug--unwrap-and-add-info frame def-name before-index after-index)
(push frame results))
(setq before-index nil
after-index nil
skip-next-lambda nil)))))
results))
(defun edebug--symbol-not-prefixed-p (sym)
"Return non-nil if SYM is a symbol not prefixed by \"edebug-\"."
(and (symbolp sym)
(not (string-prefix-p "edebug-" (symbol-name sym)))))
(defun edebug--unwrap-and-add-info (frame def-name before-index after-index)
"Update FRAME with the additional info needed by an edebug--frame.
Save DEF-NAME, BEFORE-INDEX and AFTER-INDEX in FRAME. Also
remove Edebug's instrumentation from the function and any
unevaluated arguments in FRAME."
(setf (edebug--frame-def-name frame) (and before-index def-name))
(setf (edebug--frame-before-index frame) before-index)
(setf (edebug--frame-after-index frame) after-index)
(setf (edebug--frame-fun frame) (edebug-unwrap* (edebug--frame-fun frame)))
(unless (edebug--frame-evald frame)
(let (results)
(dolist (arg (edebug--frame-args frame))
(push (edebug-unwrap* arg) results))
(setf (edebug--frame-args frame) (nreverse results)))))
;;; Trace display
......
......@@ -60,6 +60,7 @@
(require 'cl-lib)
(require 'button)
(require 'debug)
(require 'backtrace)
(require 'easymenu)
(require 'ewoc)
(require 'find-func)
......@@ -677,13 +678,6 @@ 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--print-backtrace (backtrace do-xrefs)
"Format the backtrace BACKTRACE to the current buffer."
(let ((print-escape-newlines t)
(print-level 8)
(print-length 50))
(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.
(cl-defstruct ert--test-execution-info
......@@ -732,7 +726,7 @@ run. ARGS are the arguments to `debugger'."
;; use.
;;
;; Grab the frames above the debugger.
(backtrace (cdr (backtrace-frames debugger)))
(backtrace (cdr (backtrace-get-frames debugger)))
(infos (reverse ert--infos)))
(setf (ert--test-execution-info-result info)
(cl-ecase type
......@@ -1406,9 +1400,8 @@ 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)
nil)
(insert (backtrace-to-string
(ert-test-result-with-condition-backtrace result)))
(if (not ert-batch-backtrace-right-margin)
(message "%s"
(buffer-substring-no-properties (point-min)
......@@ -2450,20 +2443,21 @@ To be used in the ERT results buffer."
(cl-etypecase result
(ert-test-passed (error "Test passed, no backtrace available"))
(ert-test-result-with-condition
(let ((backtrace (ert-test-result-with-condition-backtrace result))
(buffer (get-buffer-create "*ERT Backtrace*")))
(let ((buffer (get-buffer-create "*ERT Backtrace*")))
(pop-to-buffer buffer)
(let ((inhibit-read-only t))
(buffer-disable-undo)
(erase-buffer)
(ert-simple-view-mode)
(set-buffer-multibyte t) ; mimic debugger-setup-buffer
(setq truncate-lines t)
(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))
(insert (substitute-command-keys "':\n"))))))))
(unless (derived-mode-p 'backtrace-mode)
(backtrace-mode))
(setq backtrace-insert-header-function
(lambda () (ert--insert-backtrace-header (ert-test-name test)))
backtrace-frames (ert-test-result-with-condition-backtrace result)
backtrace-view '(:do-xrefs t))
(backtrace-print)
(goto-char (point-min)))))))
(defun ert--insert-backtrace-header (name)
(insert (substitute-command-keys "Backtrace for test `"))
(ert-insert-test-name-button name)
(insert (substitute-command-keys "':\n")))
(defun ert-results-pop-to-messages-for-test-at-point ()
"Display the part of the *Messages* buffer generated during the test at point.
......
......@@ -517,6 +517,16 @@ This will generate compile-time constants from BINDINGS."
(defvar lisp-cl-font-lock-keywords lisp-cl-font-lock-keywords-1
"Default expressions to highlight in Lisp modes.")
;; Support backtrace mode.
(defconst lisp-el-font-lock-keywords-for-backtraces lisp-el-font-lock-keywords
"Default highlighting from Emacs Lisp mod used in Backtrace mode.")
(defconst lisp-el-font-lock-keywords-for-backtraces-1 lisp-el-font-lock-keywords-1
"Subdued highlighting from Emacs Lisp mode used in Backtrace mode.")
(defconst lisp-el-font-lock-keywords-for-backtraces-2
(remove (assoc 'lisp--match-hidden-arg lisp-el-font-lock-keywords-2)
lisp-el-font-lock-keywords-2)
"Gaudy highlighting from Emacs Lisp mode used in Backtrace mode.")
(defun lisp-string-in-doc-position-p (listbeg startpos)
"Return true if a doc string may occur at STARTPOS inside a list.
LISTBEG is the position of the start of the innermost list
......
;;; backtrace-tests.el --- Tests for emacs-lisp/backtrace.el -*- lexical-binding: t; -*-
;; Copyright (C) 2018 Free Software Foundation, Inc.
;; Author: Gemini Lasswell
;; This file is part of GNU Emacs.
;; GNU Emacs is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
;;; Code:
(require 'backtrace)
(require 'ert)
(require 'seq)
;; Create a backtrace frames list with several frames.
;; TODO load this from an el file in backtrace-resources/ so the tests
;; can be byte-compiled.
(defvar backtrace-tests--frames nil)
(defun backtrace-tests--func1 (arg1 arg2)
(setq backtrace-tests--frames (backtrace-get-frames nil))
(list arg1 arg2))
(defun backtrace-tests--func2 (arg)
(list arg))
(defun backtrace-tests--func3 (arg)
(let ((foo (list 'a arg 'b)))
(list foo (backtrace-tests--func2 arg) (backtrace-tests--func1 arg 0))))
(defun backtrace-tests--create-backtrace-frames ()
(backtrace-tests--func3 "string")
;; Discard frames before this one.
(let (this-index)
(dotimes (index (length backtrace-tests--frames))
(when (eq (backtrace-frame-fun (nth index backtrace-tests--frames))
'backtrace-tests--create-backtrace-frames)
(setq this-index index)))
(setq backtrace-tests--frames (seq-subseq backtrace-tests--frames
0 (1+ this-index)))))
(backtrace-tests--create-backtrace-frames)
;; TODO check that debugger-batch-max-lines still works
(defun backtrace-tests--insert-header ()
(insert "Test header\n"))
(defmacro backtrace-tests--with-buffer (&rest body)
`(with-temp-buffer
(backtrace-mode)
(setq backtrace-frames backtrace-tests--frames)
(setq backtrace-insert-header-function #'backtrace-tests--insert-header)
(backtrace-print)
,@body))
;;; Tests
(ert-deftest backtrace-tests--to-string ()
(should (string= (backtrace-to-string backtrace-tests--frames)
" backtrace-get-frames(nil)
(setq backtrace-tests--frames (backtrace-get-frames nil))
backtrace-tests--func1(\"string\" 0)
(list foo (backtrace-tests--func2 arg) (backtrace-tests--func1 arg 0))
(let ((foo (list 'a arg 'b))) (list foo (backtrace-tests--func2 arg) (backtrace-tests--func1 arg 0)))
backtrace-tests--func3(\"string\")
backtrace-tests--create-backtrace-frames()
")))
(provide 'backtrace-tests)
;; These tests expect to see non-byte compiled stack frames.
;; Local Variables:
;; no-byte-compile: t
;; End:
;;; backtrace-tests.el ends here
......@@ -376,7 +376,7 @@ This macro is used to test if macroexpansion in `should' works."
(test (make-ert-test :body test-body))
(result (ert-run-test test)))
(should (ert-test-failed-p result))
(should (eq (nth 1 (car (ert-test-failed-backtrace result)))
(should (eq (backtrace-frame-fun (car (ert-test-failed-backtrace result)))
'signal))))
(ert-deftest ert-test-messages ()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment