github.com/spotmaxtech/k8s-apimachinery-v0260@v0.0.1/pkg/util/runtime/runtime_test.go (about) 1 /* 2 Copyright 2014 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package runtime 18 19 import ( 20 "bytes" 21 "fmt" 22 "io" 23 "net/http" 24 "os" 25 "regexp" 26 "strings" 27 "testing" 28 ) 29 30 func TestHandleCrash(t *testing.T) { 31 defer func() { 32 if x := recover(); x == nil { 33 t.Errorf("Expected a panic to recover from") 34 } 35 }() 36 defer HandleCrash() 37 panic("Test Panic") 38 } 39 40 func TestCustomHandleCrash(t *testing.T) { 41 old := PanicHandlers 42 defer func() { PanicHandlers = old }() 43 var result interface{} 44 PanicHandlers = []func(interface{}){ 45 func(r interface{}) { 46 result = r 47 }, 48 } 49 func() { 50 defer func() { 51 if x := recover(); x == nil { 52 t.Errorf("Expected a panic to recover from") 53 } 54 }() 55 defer HandleCrash() 56 panic("test") 57 }() 58 if result != "test" { 59 t.Errorf("did not receive custom handler") 60 } 61 } 62 63 func TestCustomHandleError(t *testing.T) { 64 old := ErrorHandlers 65 defer func() { ErrorHandlers = old }() 66 var result error 67 ErrorHandlers = []func(error){ 68 func(err error) { 69 result = err 70 }, 71 } 72 err := fmt.Errorf("test") 73 HandleError(err) 74 if result != err { 75 t.Errorf("did not receive custom handler") 76 } 77 } 78 79 func TestHandleCrashLog(t *testing.T) { 80 log, err := captureStderr(func() { 81 defer func() { 82 if r := recover(); r == nil { 83 t.Fatalf("expected a panic to recover from") 84 } 85 }() 86 defer HandleCrash() 87 panic("test panic") 88 }) 89 if err != nil { 90 t.Fatalf("%v", err) 91 } 92 // Example log: 93 // 94 // ...] Observed a panic: test panic 95 // goroutine 6 [running]: 96 // command-line-arguments.logPanic(0x..., 0x...) 97 // .../src/k8s.io/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/runtime/runtime.go:69 +0x... 98 lines := strings.Split(log, "\n") 99 if len(lines) < 4 { 100 t.Fatalf("panic log should have 1 line of message, 1 line per goroutine and 2 lines per function call") 101 } 102 if match, _ := regexp.MatchString("Observed a panic: test panic", lines[0]); !match { 103 t.Errorf("mismatch panic message: %s", lines[0]) 104 } 105 // The following regexp's verify that Kubernetes panic log matches Golang stdlib 106 // stacktrace pattern. We need to update these regexp's if stdlib changes its pattern. 107 if match, _ := regexp.MatchString(`goroutine [0-9]+ \[.+\]:`, lines[1]); !match { 108 t.Errorf("mismatch goroutine: %s", lines[1]) 109 } 110 if match, _ := regexp.MatchString(`logPanic(.*)`, lines[2]); !match { 111 t.Errorf("mismatch symbolized function name: %s", lines[2]) 112 } 113 if match, _ := regexp.MatchString(`runtime\.go:[0-9]+ \+0x`, lines[3]); !match { 114 t.Errorf("mismatch file/line/offset information: %s", lines[3]) 115 } 116 } 117 118 func TestHandleCrashLogSilenceHTTPErrAbortHandler(t *testing.T) { 119 log, err := captureStderr(func() { 120 defer func() { 121 if r := recover(); r != http.ErrAbortHandler { 122 t.Fatalf("expected to recover from http.ErrAbortHandler") 123 } 124 }() 125 defer HandleCrash() 126 panic(http.ErrAbortHandler) 127 }) 128 if err != nil { 129 t.Fatalf("%v", err) 130 } 131 if len(log) > 0 { 132 t.Fatalf("expected no stderr log, got: %s", log) 133 } 134 } 135 136 // captureStderr redirects stderr to result string, and then restore stderr from backup 137 func captureStderr(f func()) (string, error) { 138 r, w, err := os.Pipe() 139 if err != nil { 140 return "", err 141 } 142 bak := os.Stderr 143 os.Stderr = w 144 defer func() { os.Stderr = bak }() 145 146 resultCh := make(chan string) 147 // copy the output in a separate goroutine so printing can't block indefinitely 148 go func() { 149 var buf bytes.Buffer 150 io.Copy(&buf, r) 151 resultCh <- buf.String() 152 }() 153 154 f() 155 w.Close() 156 157 return <-resultCh, nil 158 }