...
1 package panics
2
3 import (
4 "errors"
5 "fmt"
6 "runtime"
7 "sync"
8 "testing"
9
10 "github.com/stretchr/testify/assert"
11 "github.com/stretchr/testify/require"
12 )
13
14 func ExampleCatcher() {
15 var pc Catcher
16 i := 0
17 pc.Try(func() { i += 1 })
18 pc.Try(func() { panic("abort!") })
19 pc.Try(func() { i += 1 })
20
21 rc := pc.Recovered()
22
23 fmt.Println(i)
24 fmt.Println(rc.Value.(string))
25
26
27
28 }
29
30 func ExampleCatcher_callers() {
31 var pc Catcher
32 pc.Try(func() { panic("mayday!") })
33
34 recovered := pc.Recovered()
35
36
37
38
39
40 frames := runtime.CallersFrames(recovered.Callers)
41 for {
42 frame, more := frames.Next()
43
44 fmt.Println(frame.Function)
45
46 if !more {
47 break
48 }
49 }
50
51
52
53
54
55
56
57
58
59
60
61
62 }
63
64 func ExampleCatcher_error() {
65 helper := func() error {
66 var pc Catcher
67 pc.Try(func() { panic(errors.New("error")) })
68 return pc.Recovered().AsError()
69 }
70
71 if err := helper(); err != nil {
72
73
74
75
76 if cause := errors.Unwrap(err); cause != nil {
77 fmt.Printf("helper panicked with an error: %s", cause)
78 }
79 }
80
81
82 }
83
84 func TestCatcher(t *testing.T) {
85 t.Parallel()
86
87 err1 := errors.New("SOS")
88
89 t.Run("error", func(t *testing.T) {
90 t.Parallel()
91 var pc Catcher
92 pc.Try(func() { panic(err1) })
93 recovered := pc.Recovered()
94 require.ErrorIs(t, recovered.AsError(), err1)
95 require.ErrorAs(t, recovered.AsError(), &err1)
96
97
98
99 require.Contains(t, recovered.String(), "SOS", "formatted panic should contain the panic message")
100 require.Contains(t, recovered.String(), "panics.(*Catcher).Try", recovered.String(), "formatted panic should contain the stack trace")
101 })
102
103 t.Run("not error", func(t *testing.T) {
104 var pc Catcher
105 pc.Try(func() { panic("definitely not an error") })
106 recovered := pc.Recovered()
107 require.NotErrorIs(t, recovered.AsError(), err1)
108 require.Nil(t, errors.Unwrap(recovered.AsError()))
109 })
110
111 t.Run("repanic panics", func(t *testing.T) {
112 var pc Catcher
113 pc.Try(func() { panic(err1) })
114 require.Panics(t, pc.Repanic)
115 })
116
117 t.Run("repanic does not panic without child panic", func(t *testing.T) {
118 t.Parallel()
119 var pc Catcher
120 pc.Try(func() { _ = 1 })
121 require.NotPanics(t, pc.Repanic)
122 })
123
124 t.Run("is goroutine safe", func(t *testing.T) {
125 t.Parallel()
126 var wg sync.WaitGroup
127 var pc Catcher
128 for i := 0; i < 100; i++ {
129 i := i
130 wg.Add(1)
131 func() {
132 defer wg.Done()
133 pc.Try(func() {
134 if i == 50 {
135 panic("50")
136 }
137
138 })
139 }()
140 }
141 wg.Wait()
142 require.Equal(t, "50", pc.Recovered().Value)
143 })
144 }
145
146 func TestRecoveredAsError(t *testing.T) {
147 t.Parallel()
148 t.Run("as error is nil", func(t *testing.T) {
149 t.Parallel()
150 fn := func() error {
151 var c Catcher
152 c.Try(func() {})
153 return c.Recovered().AsError()
154 }
155 err := fn()
156 assert.Nil(t, err)
157 })
158
159 t.Run("as error is not nil nil", func(t *testing.T) {
160 t.Parallel()
161 fn := func() error {
162 var c Catcher
163 c.Try(func() { panic("oh dear!") })
164 return c.Recovered().AsError()
165 }
166 err := fn()
167 assert.NotNil(t, err)
168 })
169 }
170
View as plain text