Skip to content

Commit

Permalink
format stacktrace in formatter, change EnableStacktrace boolean to mo…
Browse files Browse the repository at this point in the history
…de enum
  • Loading branch information
diPhantxm committed Apr 27, 2023
1 parent 9d9d7bf commit 71795fe
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 14 deletions.
2 changes: 1 addition & 1 deletion assertion.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ type AssertionFailure struct {
Delta *AssertionValue

// Stacktrace of a fail
Stacktrace string
Stacktrace []CallerInfo
}

// AssertionValue holds expected or actual value
Expand Down
42 changes: 40 additions & 2 deletions chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package httpexpect

import (
"fmt"
"runtime/debug"
"runtime"
"sync"
"testing"

Expand Down Expand Up @@ -437,7 +437,7 @@ func (c *chain) fail(failure AssertionFailure) {
failure.IsFatal = true
}

failure.Stacktrace = string(debug.Stack())
failure.Stacktrace = GetCallerInfo()

c.failure = &failure
}
Expand Down Expand Up @@ -506,3 +506,41 @@ func isTestingTB(in AssertionHandler) bool {
}
return false
}

type CallerInfo struct {
FuncName string
File string
Line int
}

func GetCallerInfo() []CallerInfo {
callers := []CallerInfo{}
for i := 0; ; i++ {
pc, file, line, ok := runtime.Caller(i)
if !ok {
break
}

if file == "<autogenerated>" {
break
}

f := runtime.FuncForPC(pc)
if f == nil {
break
}
funcName := f.Name()

if funcName == "testing.tRunner" {
break
}

callers = append(callers, CallerInfo{
FuncName: funcName,
File: file,
Line: line,
})
}

return callers
}
1 change: 0 additions & 1 deletion chain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -688,7 +688,6 @@ func TestChain_Stacktrace(t *testing.T) {
assert.True(t, opChain.failed())
assert.NotNil(t, handler.failure)
assert.NotEmpty(t, handler.failure.Stacktrace)
assert.Contains(t, handler.failure.Stacktrace, "TestChain_Stacktrace")
}

func TestChain_Reporting(t *testing.T) {
Expand Down
24 changes: 19 additions & 5 deletions formatter.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ type DefaultFormatter struct {
DisableResponses bool

// Enables printing of stacktrace on failure
EnableStacktrace bool
StacktraceMode StacktraceMode

// Thousand separator.
// Default is DigitSeparatorUnderscore.
Expand Down Expand Up @@ -115,6 +115,16 @@ func (f *DefaultFormatter) FormatFailure(
}
}

type StacktraceMode int

const (
// Unconditionally disable stacktrace.
StacktraceModeDisabled StacktraceMode = iota

// Format caller info as `at [function name]([file]:[line])`
StacktraceModeDefault
)

// DigitSeparator defines the separator used to format integers and floats.
type DigitSeparator int

Expand Down Expand Up @@ -558,11 +568,15 @@ func (f *DefaultFormatter) fillStacktrace(
) {
data.Stacktrace = []string{}

if f.EnableStacktrace {
if len(data.Stacktrace) != 0 {
data.Stacktrace = strings.Split(failure.Stacktrace, "\n\t")
data.HaveStacktrace = true
if f.StacktraceMode == StacktraceModeDisabled {
return
}
if f.StacktraceMode == StacktraceModeDefault {
for _, call := range failure.Stacktrace {
formatted := fmt.Sprintf("at %s(%s:%d)", call.FuncName, call.File, call.Line)
data.Stacktrace = append(data.Stacktrace, formatted)
}
data.HaveStacktrace = len(failure.Stacktrace) != 0
}
}

Expand Down
42 changes: 37 additions & 5 deletions formatter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1059,13 +1059,45 @@ func TestFormatter_ColorMode(t *testing.T) {

func TestFormatter_Stacktrace(t *testing.T) {
df := &DefaultFormatter{
EnableStacktrace: true,
StacktraceMode: StacktraceModeDefault,
}
ctx := &AssertionContext{}

fl := &AssertionFailure{
Stacktrace: "Foo()\n\tBar()\n\tBuzz()",
cases := []struct {
callerInfo CallerInfo
want string
}{
{
CallerInfo{
FuncName: "Foo()",
File: "formatter_test.go",
Line: 228,
},
"at Foo()(formatter_test.go:228)",
},
{
CallerInfo{
FuncName: "Bar()",
File: "formatter.go",
Line: 123,
},
"at Bar()(formatter.go:123)",
},
{
CallerInfo{
FuncName: "Buzz()",
File: "file.go",
Line: 5,
},
"at Buzz()(file.go:5)",
},
}

for _, tc := range cases {
fl := &AssertionFailure{
Stacktrace: []CallerInfo{tc.callerInfo},
}
fd := df.buildFormatData(ctx, fl)
assert.Equal(t, []string{tc.want}, fd.Stacktrace)
}
fd := df.buildFormatData(ctx, fl)
assert.Equal(t, []string{"Foo()", "Bar()", "Buzz()"}, fd.Stacktrace)
}

0 comments on commit 71795fe

Please sign in to comment.