-
Notifications
You must be signed in to change notification settings - Fork 5
/
panic_test.go
166 lines (141 loc) · 4.85 KB
/
panic_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
package iopipe
import (
"fmt"
"runtime"
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func testFunctionThatPanics() {
panic(fmt.Errorf("i am a throwing function"))
}
func TestPanic_NewPanicInvocationError(t *testing.T) {
Convey("Given a parent function that recovers from panics, stores the error using NewPanicInvocationError, and wraps a panicking child function", t, func() {
var panicErr *InvocationError
parentWithPanickyChild := func() {
defer func() {
if err := recover(); err != nil {
panicErr = NewPanicInvocationError(err)
}
}()
testFunctionThatPanics()
}
Convey("The child function should panic", func() {
So(testFunctionThatPanics, ShouldPanic)
})
Convey("The parent function should not panic", func() {
So(parentWithPanickyChild, ShouldNotPanic)
})
Convey("When the parent function is called", func() {
parentWithPanickyChild()
Convey("The first stack frame in NewPanicInvocationError refers to the panic inside the child function", func() {
So(*panicErr.StackTrace[0], ShouldResemble, panicErrorStackFrame{
Path: "github.com/iopipe/iopipe-go/panic_test.go",
Line: 12,
Function: "testFunctionThatPanics",
})
})
})
})
}
func assertPanicMessage(t *testing.T, panicFunc func(), expectedMessage string) {
defer func() {
if err := recover(); err != nil {
panicInfo := getPanicInfo(err, 0)
So(panicInfo, ShouldNotBeNil)
So(panicInfo.Message, ShouldNotBeNil)
So(panicInfo.Message, ShouldEqual, expectedMessage)
So(panicInfo.StackTrace, ShouldNotBeNil)
}
}()
panicFunc()
t.Errorf("Should have exited due to panic")
}
func TestPanic_FormattingStringValue(t *testing.T) {
Convey("Panic formatting is accurate for String message values", t, func() {
assertPanicMessage(t, func() { panic("Panic time!") }, "Panic time!")
})
}
func TestPanic_formattingIntValue(t *testing.T) {
Convey("Panic formatting is accurate for Int message values", t, func() {
assertPanicMessage(t, func() { panic(1234) }, "1234")
})
}
type CustomError struct{}
func (e CustomError) Error() string { return fmt.Sprintf("Something bad happened!") }
func TestPanic_formattingCustomError(t *testing.T) {
Convey("Panic formatting is accurate for Customer Error type messages", t, func() {
customError := &CustomError{}
assertPanicMessage(t, func() { panic(customError) }, customError.Error())
})
}
func TestPanic_formatFrame(t *testing.T) {
Convey("FormatFrame appropriately formats a variety of runtime.Frame examples", t, func() {
var tests = []struct {
inputPath string
inputLine int32
inputFunction string
expectedPath string
expectedLine int32
expectedFunction string
}{
{
inputPath: "/Volumes/Unix/workspace/LambdaGoLang/src/GoAmzn-Github-Aws-AwsLambdaGo/src/github.com/aws/aws-lambda-go/lambda/panic_test.go",
inputLine: 42,
inputFunction: "github.com/aws/aws-lambda-go/lambda.printStack",
expectedPath: "github.com/aws/aws-lambda-go/lambda/panic_test.go",
expectedLine: 42,
expectedFunction: "printStack",
},
{
inputPath: "/home/user/src/pkg/sub/file.go",
inputLine: 42,
inputFunction: "pkg/sub.Type.Method",
expectedPath: "pkg/sub/file.go",
expectedLine: 42,
expectedFunction: "Type.Method",
},
{
inputPath: "/home/user/src/pkg/sub/sub2/file.go",
inputLine: 42,
inputFunction: "pkg/sub/sub2.Type.Method",
expectedPath: "pkg/sub/sub2/file.go",
expectedLine: 42,
expectedFunction: "Type.Method",
},
{
inputPath: "/home/user/src/pkg/file.go",
inputLine: 101,
inputFunction: "pkg.Type.Method",
expectedPath: "pkg/file.go",
expectedLine: 101,
expectedFunction: "Type.Method",
},
}
for _, test := range tests {
inputFrame := runtime.Frame{
File: test.inputPath,
Line: int(test.inputLine),
Function: test.inputFunction,
}
actual := formatFrame(inputFrame)
Convey(fmt.Sprintf(`Frame with inputPath "%s" should be formatted correctly`, test.inputPath), func() {
So(actual.Path, ShouldEqual, test.expectedPath)
So(actual.Line, ShouldEqual, test.expectedLine)
So(actual.Function, ShouldEqual, test.expectedFunction)
})
}
})
}
func TestPanic_runtimeStackTrace(t *testing.T) {
Convey("Runtime stack trace's first element is where the panic happens", t, func() {
const framesToHide = framesToPanicInfo
panicInfo := getPanicInfo("Panic time!", framesToHide)
So(panicInfo, ShouldNotBeNil)
So(panicInfo.StackTrace, ShouldNotBeNil)
So(len(panicInfo.StackTrace), ShouldBeGreaterThan, 0)
frame := panicInfo.StackTrace[0]
So(frame.Path, ShouldEqual, "github.com/iopipe/iopipe-go/panic_test.go")
// So(frame.Line, ShouldEqual, 153)
So(frame.Function, ShouldEqual, "TestPanic_runtimeStackTrace.func1")
})
}