Skip to content

Commit 30da670

Browse files
authored
Merge feature/panic-stack-trace-chained
2 parents 1eb1095 + 8075691 commit 30da670

File tree

2 files changed

+77
-15
lines changed

2 files changed

+77
-15
lines changed

panic.go

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -38,33 +38,29 @@ func ErrorFromPanic(recoverResult interface{}) (error, bool) {
3838
}
3939

4040
if wrapper, ok := err.(*panicErrorWrapper); ok {
41-
return wrapper.errorWithStackTrace, true
41+
return wrapper.inner, true
4242
}
4343

4444
return err, true
4545
}
4646

4747
func newPanicErrorWrapper(err error) *panicErrorWrapper {
48-
originalError, errWithStackTrace := err, err
49-
if typedErr, ok := errWithStackTrace.(*Error); !ok || typedErr.stackTrace == nil {
50-
builder := NewErrorBuilder(panicPayloadWrap).WithConditionallyFormattedMessage("").WithCause(err)
51-
errWithStackTrace = builder.Create()
52-
}
53-
5448
return &panicErrorWrapper{
55-
originalError: originalError,
56-
errorWithStackTrace: errWithStackTrace,
49+
inner: NewErrorBuilder(panicPayloadWrap).
50+
WithConditionallyFormattedMessage("panic").
51+
WithCause(err).
52+
EnhanceStackTrace().
53+
Create(),
5754
}
5855
}
5956

57+
// panicErrorWrapper is designed for the original stack trace not to be lost in any way it may be handled
6058
type panicErrorWrapper struct {
61-
originalError error
62-
errorWithStackTrace error
59+
inner error
6360
}
6461

65-
// Original error is used, as the wrapped one contains only the same stack trace as the panic
6662
func (w *panicErrorWrapper) Error() string {
67-
return fmt.Sprintf("%+v", w.originalError)
63+
return fmt.Sprintf("%+v", w.inner)
6864
}
6965

7066
func (w *panicErrorWrapper) String() string {

panic_test.go

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"errors"
55
"fmt"
66
"testing"
7+
"time"
78

89
"github.com/stretchr/testify/require"
910
)
@@ -29,8 +30,7 @@ func TestPanicErrorx(t *testing.T) {
2930
output := fmt.Sprintf("%v", r)
3031

3132
require.Contains(t, output, "awful", output)
32-
// original error was non-errorx, no use of adding panic callstack to message
33-
require.NotContains(t, output, "errorx.funcWithBadPanic()", output)
33+
require.Contains(t, output, "errorx.funcWithBadPanic()", output)
3434
}()
3535

3636
funcWithBadPanic()
@@ -111,3 +111,69 @@ func funcWithBadPanic() {
111111
func funcWithBadErr() error {
112112
return errors.New("awful")
113113
}
114+
115+
func TestPanicChain(t *testing.T) {
116+
ch0 := make(chan error, 1)
117+
ch1 := make(chan error, 1)
118+
119+
go doMischief(ch1)
120+
go doMoreMischief(ch0, ch1)
121+
122+
select {
123+
case err := <-ch0:
124+
require.Error(t, err)
125+
require.False(t, IsOfType(err, AssertionFailed))
126+
output := fmt.Sprintf("%+v", err)
127+
require.Contains(t, output, "mischiefProper", output)
128+
require.Contains(t, output, "mischiefAsPanic", output)
129+
require.Contains(t, output, "doMischief", output)
130+
require.Contains(t, output, "handleMischief", output)
131+
require.NotContains(t, output, "doMoreMischief", output) // stack trace is only enhanced in Panic, not in user code
132+
t.Log(output)
133+
case <-time.After(time.Second):
134+
require.Fail(t, "expected error")
135+
}
136+
}
137+
138+
func doMoreMischief(ch0 chan error, ch1 chan error) {
139+
defer func() {
140+
if e := recover(); e != nil {
141+
err, ok := ErrorFromPanic(e)
142+
if ok {
143+
ch0 <- Decorate(err, "hop 2")
144+
return
145+
}
146+
}
147+
ch0 <- AssertionFailed.New("test failed")
148+
}()
149+
150+
handleMischief(ch1)
151+
}
152+
153+
func handleMischief(ch chan error) {
154+
err := <-ch
155+
Panic(Decorate(err, "handle"))
156+
}
157+
158+
func doMischief(ch chan error) {
159+
defer func() {
160+
if e := recover(); e != nil {
161+
err, ok := ErrorFromPanic(e)
162+
if ok {
163+
ch <- Decorate(err, "hop 1")
164+
return
165+
}
166+
}
167+
ch <- AssertionFailed.New("test failed")
168+
}()
169+
170+
mischiefAsPanic()
171+
}
172+
173+
func mischiefAsPanic() {
174+
Panic(mischiefProper())
175+
}
176+
177+
func mischiefProper() error {
178+
return ExternalError.New("mischief")
179+
}

0 commit comments

Comments
 (0)