closes #54 will print stack trace if case of panic

Этот коммит содержится в:
gedi 2016-11-22 21:00:43 +02:00
родитель dbda8afb77
коммит bfb741f6c7
6 изменённых файлов: 139 добавлений и 6 удалений

Просмотреть файл

@ -8,6 +8,7 @@ import (
"net/http/httptest" "net/http/httptest"
"strings" "strings"
txdb "github.com/DATA-DOG/go-txdb"
"github.com/DATA-DOG/godog" "github.com/DATA-DOG/godog"
"github.com/DATA-DOG/godog/gherkin" "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()) f.printStep(res.step, res.def, res.typ.clr())
if res.err != nil { 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 { if res.typ == pending {
fmt.Fprintln(f.out, s(f.indent*3)+yellow("TODO: write pending definition")) 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") fmt.Fprintln(f.out, "\n--- "+red("Failed steps:")+"\n")
for _, fail := range f.failed { 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(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() f.basefmt.Summary()

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)
}
}

Просмотреть файл

@ -220,10 +220,9 @@ func (s *Suite) runStep(step *gherkin.Step, prevStepErr error) (err error) {
defer func() { defer func() {
if e := recover(); e != nil { if e := recover(); e != nil {
var ok bool err = &traceError{
err, ok = e.(error) msg: fmt.Sprintf("%v", e),
if !ok { stack: callStack(),
err = fmt.Errorf("%v", e)
} }
} }
switch err { switch err {

Просмотреть файл

@ -287,6 +287,7 @@ func (s *suiteContext) iRunFeatureSuite() error {
s.testedSuite.fmt = testFormatterFunc("godog", &s.out) s.testedSuite.fmt = testFormatterFunc("godog", &s.out)
s.testedSuite.run() s.testedSuite.run()
s.testedSuite.fmt.Summary() s.testedSuite.fmt.Summary()
return nil return nil
} }