...
1
16
17 package runtime
18
19 import (
20 "bytes"
21 "fmt"
22 "io"
23 "net/http"
24 "os"
25 "regexp"
26 "strings"
27 "sync"
28 "testing"
29 "time"
30 )
31
32 func TestHandleCrash(t *testing.T) {
33 defer func() {
34 if x := recover(); x == nil {
35 t.Errorf("Expected a panic to recover from")
36 }
37 }()
38 defer HandleCrash()
39 panic("Test Panic")
40 }
41
42 func TestCustomHandleCrash(t *testing.T) {
43 old := PanicHandlers
44 defer func() { PanicHandlers = old }()
45 var result interface{}
46 PanicHandlers = []func(interface{}){
47 func(r interface{}) {
48 result = r
49 },
50 }
51 func() {
52 defer func() {
53 if x := recover(); x == nil {
54 t.Errorf("Expected a panic to recover from")
55 }
56 }()
57 defer HandleCrash()
58 panic("test")
59 }()
60 if result != "test" {
61 t.Errorf("did not receive custom handler")
62 }
63 }
64
65 func TestCustomHandleError(t *testing.T) {
66 old := ErrorHandlers
67 defer func() { ErrorHandlers = old }()
68 var result error
69 ErrorHandlers = []func(error){
70 func(err error) {
71 result = err
72 },
73 }
74 err := fmt.Errorf("test")
75 HandleError(err)
76 if result != err {
77 t.Errorf("did not receive custom handler")
78 }
79 }
80
81 func TestHandleCrashLog(t *testing.T) {
82 log, err := captureStderr(func() {
83 defer func() {
84 if r := recover(); r == nil {
85 t.Fatalf("expected a panic to recover from")
86 }
87 }()
88 defer HandleCrash()
89 panic("test panic")
90 })
91 if err != nil {
92 t.Fatalf("%v", err)
93 }
94
95
96
97
98
99
100 lines := strings.Split(log, "\n")
101 if len(lines) < 4 {
102 t.Fatalf("panic log should have 1 line of message, 1 line per goroutine and 2 lines per function call")
103 }
104 if match, _ := regexp.MatchString("Observed a panic: test panic", lines[0]); !match {
105 t.Errorf("mismatch panic message: %s", lines[0])
106 }
107
108
109 if match, _ := regexp.MatchString(`goroutine [0-9]+ \[.+\]:`, lines[1]); !match {
110 t.Errorf("mismatch goroutine: %s", lines[1])
111 }
112 if match, _ := regexp.MatchString(`logPanic(.*)`, lines[2]); !match {
113 t.Errorf("mismatch symbolized function name: %s", lines[2])
114 }
115 if match, _ := regexp.MatchString(`runtime\.go:[0-9]+ \+0x`, lines[3]); !match {
116 t.Errorf("mismatch file/line/offset information: %s", lines[3])
117 }
118 }
119
120 func TestHandleCrashLogSilenceHTTPErrAbortHandler(t *testing.T) {
121 log, err := captureStderr(func() {
122 defer func() {
123 if r := recover(); r != http.ErrAbortHandler {
124 t.Fatalf("expected to recover from http.ErrAbortHandler")
125 }
126 }()
127 defer HandleCrash()
128 panic(http.ErrAbortHandler)
129 })
130 if err != nil {
131 t.Fatalf("%v", err)
132 }
133 if len(log) > 0 {
134 t.Fatalf("expected no stderr log, got: %s", log)
135 }
136 }
137
138
139 func captureStderr(f func()) (string, error) {
140 r, w, err := os.Pipe()
141 if err != nil {
142 return "", err
143 }
144 bak := os.Stderr
145 os.Stderr = w
146 defer func() { os.Stderr = bak }()
147
148 resultCh := make(chan string)
149
150 go func() {
151 var buf bytes.Buffer
152 io.Copy(&buf, r)
153 resultCh <- buf.String()
154 }()
155
156 f()
157 w.Close()
158
159 return <-resultCh, nil
160 }
161
162 func Test_rudimentaryErrorBackoff_OnError_ParallelSleep(t *testing.T) {
163 r := &rudimentaryErrorBackoff{
164 minPeriod: time.Second,
165 }
166
167 start := make(chan struct{})
168 var wg sync.WaitGroup
169 for i := 0; i < 30; i++ {
170 wg.Add(1)
171 go func() {
172 <-start
173 r.OnError(nil)
174 wg.Done()
175 }()
176 }
177 st := time.Now()
178 close(start)
179 wg.Wait()
180
181 if since := time.Since(st); since > 5*time.Second {
182 t.Errorf("OnError slept for too long: %s", since)
183 }
184 }
185
View as plain text