closes #54 will print stack trace if case of panic
Этот коммит содержится в:
родитель
dbda8afb77
коммит
bfb741f6c7
6 изменённых файлов: 139 добавлений и 6 удалений
|
@ -8,6 +8,7 @@ import (
|
|||
"net/http/httptest"
|
||||
"strings"
|
||||
|
||||
txdb "github.com/DATA-DOG/go-txdb"
|
||||
"github.com/DATA-DOG/godog"
|
||||
"github.com/DATA-DOG/godog/gherkin"
|
||||
)
|
||||
|
|
|
@ -308,7 +308,7 @@ func (f *pretty) printStepKind(res *stepResult) {
|
|||
|
||||
f.printStep(res.step, res.def, res.typ.clr())
|
||||
if res.err != nil {
|
||||
fmt.Fprintln(f.out, s(f.indent*2)+redb(res.err))
|
||||
fmt.Fprintln(f.out, s(f.indent*2)+redb(fmt.Sprintf("%+v", res.err)))
|
||||
}
|
||||
if res.typ == pending {
|
||||
fmt.Fprintln(f.out, s(f.indent*3)+yellow("TODO: write pending definition"))
|
||||
|
|
|
@ -59,7 +59,7 @@ func (f *progress) Summary() {
|
|||
fmt.Fprintln(f.out, "\n--- "+red("Failed steps:")+"\n")
|
||||
for _, fail := range f.failed {
|
||||
fmt.Fprintln(f.out, s(4)+red(fail.step.Keyword+" "+fail.step.Text)+black(" # "+fail.line()))
|
||||
fmt.Fprintln(f.out, s(6)+red("Error: ")+redb(fail.err)+"\n")
|
||||
fmt.Fprintln(f.out, s(6)+red("Error: ")+redb(fmt.Sprintf("%+v", fail.err))+"\n")
|
||||
}
|
||||
}
|
||||
f.basefmt.Summary()
|
||||
|
|
132
stacktrace.go
Обычный файл
132
stacktrace.go
Обычный файл
|
@ -0,0 +1,132 @@
|
|||
package godog
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"path"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Frame represents a program counter inside a stack frame.
|
||||
type stackFrame uintptr
|
||||
|
||||
// pc returns the program counter for this frame;
|
||||
// multiple frames may have the same PC value.
|
||||
func (f stackFrame) pc() uintptr { return uintptr(f) - 1 }
|
||||
|
||||
// file returns the full path to the file that contains the
|
||||
// function for this Frame's pc.
|
||||
func (f stackFrame) file() string {
|
||||
fn := runtime.FuncForPC(f.pc())
|
||||
if fn == nil {
|
||||
return "unknown"
|
||||
}
|
||||
file, _ := fn.FileLine(f.pc())
|
||||
return file
|
||||
}
|
||||
|
||||
// line returns the line number of source code of the
|
||||
// function for this Frame's pc.
|
||||
func (f stackFrame) line() int {
|
||||
fn := runtime.FuncForPC(f.pc())
|
||||
if fn == nil {
|
||||
return 0
|
||||
}
|
||||
_, line := fn.FileLine(f.pc())
|
||||
return line
|
||||
}
|
||||
|
||||
// Format formats the frame according to the fmt.Formatter interface.
|
||||
//
|
||||
// %s source file
|
||||
// %d source line
|
||||
// %n function name
|
||||
// %v equivalent to %s:%d
|
||||
//
|
||||
// Format accepts flags that alter the printing of some verbs, as follows:
|
||||
//
|
||||
// %+s path of source file relative to the compile time GOPATH
|
||||
// %+v equivalent to %+s:%d
|
||||
func (f stackFrame) Format(s fmt.State, verb rune) {
|
||||
funcname := func(name string) string {
|
||||
i := strings.LastIndex(name, "/")
|
||||
name = name[i+1:]
|
||||
i = strings.Index(name, ".")
|
||||
return name[i+1:]
|
||||
}
|
||||
|
||||
switch verb {
|
||||
case 's':
|
||||
switch {
|
||||
case s.Flag('+'):
|
||||
pc := f.pc()
|
||||
fn := runtime.FuncForPC(pc)
|
||||
if fn == nil {
|
||||
io.WriteString(s, "unknown")
|
||||
} else {
|
||||
file, _ := fn.FileLine(pc)
|
||||
fmt.Fprintf(s, "%s\n\t%s", fn.Name(), file)
|
||||
}
|
||||
default:
|
||||
io.WriteString(s, path.Base(f.file()))
|
||||
}
|
||||
case 'd':
|
||||
fmt.Fprintf(s, "%d", f.line())
|
||||
case 'n':
|
||||
name := runtime.FuncForPC(f.pc()).Name()
|
||||
io.WriteString(s, funcname(name))
|
||||
case 'v':
|
||||
f.Format(s, 's')
|
||||
io.WriteString(s, ":")
|
||||
f.Format(s, 'd')
|
||||
}
|
||||
}
|
||||
|
||||
// stack represents a stack of program counters.
|
||||
type stack []uintptr
|
||||
|
||||
func (s *stack) Format(st fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
switch {
|
||||
case st.Flag('+'):
|
||||
for _, pc := range *s {
|
||||
f := stackFrame(pc)
|
||||
fmt.Fprintf(st, "\n%+v", f)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func callStack() *stack {
|
||||
const depth = 32
|
||||
var pcs [depth]uintptr
|
||||
n := runtime.Callers(3, pcs[:])
|
||||
var st stack = pcs[0:n]
|
||||
return &st
|
||||
}
|
||||
|
||||
// fundamental is an error that has a message and a stack, but no caller.
|
||||
type traceError struct {
|
||||
msg string
|
||||
*stack
|
||||
}
|
||||
|
||||
func (f *traceError) Error() string { return f.msg }
|
||||
|
||||
func (f *traceError) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
if s.Flag('+') {
|
||||
io.WriteString(s, f.msg)
|
||||
f.stack.Format(s, verb)
|
||||
return
|
||||
}
|
||||
fallthrough
|
||||
case 's':
|
||||
io.WriteString(s, f.msg)
|
||||
case 'q':
|
||||
fmt.Fprintf(s, "%q", f.msg)
|
||||
}
|
||||
}
|
7
suite.go
7
suite.go
|
@ -220,10 +220,9 @@ func (s *Suite) runStep(step *gherkin.Step, prevStepErr error) (err error) {
|
|||
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
var ok bool
|
||||
err, ok = e.(error)
|
||||
if !ok {
|
||||
err = fmt.Errorf("%v", e)
|
||||
err = &traceError{
|
||||
msg: fmt.Sprintf("%v", e),
|
||||
stack: callStack(),
|
||||
}
|
||||
}
|
||||
switch err {
|
||||
|
|
|
@ -287,6 +287,7 @@ func (s *suiteContext) iRunFeatureSuite() error {
|
|||
s.testedSuite.fmt = testFormatterFunc("godog", &s.out)
|
||||
s.testedSuite.run()
|
||||
s.testedSuite.fmt.Summary()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче