github.com/haraldrudell/parl@v0.4.176/perrors/panicdetector/indices_test.go (about) 1 /* 2 © 2022–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/) 3 ISC License 4 */ 5 6 package panicdetector 7 8 import ( 9 "errors" 10 "fmt" 11 "runtime" 12 "testing" 13 14 "github.com/haraldrudell/parl/pruntime" 15 ) 16 17 const ( 18 // value indicator for indices() 19 panicYes = true 20 // value indicator for indices() 21 panicNo = false 22 ) 23 24 func TestIndices(t *testing.T) { 25 var testList = []struct { 26 name string 27 stackGenerator func() (stack pruntime.Stack, panicLoc, stackLoc *pruntime.CodeLocation) 28 isPanic bool 29 }{ 30 {"no panic", noPanic, panicNo}, 31 {"invoke panic()", invokePanic, panicYes}, 32 {"nil pointer dereference", nilPointerDereference, panicYes}, 33 {"bad slice", slicePanic, panicYes}, 34 } 35 36 var stackAct pruntime.Stack 37 var isPanicAct bool 38 var stackIndexAct, panicIndexAct int 39 var panicLocExp, stackLocExp *pruntime.CodeLocation 40 for _, params := range testList { 41 stackAct, panicLocExp, stackLocExp = params.stackGenerator() 42 43 if params.isPanic { 44 // INPUTSTACK for test: panic(1) stack:ID: 4 IsMain: false status: running 45 // github.com/haraldrudell/parl/perrors/errorglue.panicOneDefer(0x1, 0x1400006ce08) 46 // indices_test.go:99 47 // panic({0x102dcdba0?, 0x102eb3188?}) 48 // panic.go:914 49 // github.com/haraldrudell/parl/perrors/errorglue.panicOne() 50 // indices_test.go:88 51 // github.com/haraldrudell/parl/perrors/errorglue.TestIndices(0x140000036c0) 52 // indices_test.go:43 53 // testing.tRunner(0x140000036c0, 0x102df5de8) 54 // testing.go:1595 55 // cre: testing.(*T).Run-testing.go:1648 in goroutine 1 1 56 // INPUTSTACK for test: nil pointer dereference stack:ID: 4 IsMain: false status: running 57 // github.com/haraldrudell/parl/perrors/errorglue.panicNilPointerDefer({0x102d95195, 0x40}, 0x14000123e00) 58 // indices_test.go:134 59 // panic({0x102dd5740?, 0x102ebb950?}) 60 // panic.go:914 61 // github.com/haraldrudell/parl/perrors/errorglue.panicNilPointer() 62 // indices_test.go:113 63 // github.com/haraldrudell/parl/perrors/errorglue.TestIndices(0x140000036c0) 64 // indices_test.go:43 65 // testing.tRunner(0x140000036c0, 0x102df5de8) 66 // testing.go:1595 67 // cre: testing.(*T).Run-testing.go:1648 in goroutine 1 1 68 t.Logf("INPUTSTACK for test: ‘%s’ stack:%s", params.name, stackAct) 69 } 70 71 isPanicAct, stackIndexAct, panicIndexAct = Indices(stackAct) 72 73 // isPanic should match 74 if isPanicAct != params.isPanic { 75 t.Errorf("FAIL test ‘%s’ Indices() isPanic %t exp %t", 76 params.name, 77 isPanicAct, params.isPanic, 78 ) 79 continue 80 } else if !isPanicAct { 81 continue 82 } else if stackLocExp == nil || panicLocExp == nil { 83 t.Fatalf("FAIL CORRUPTION test ‘%s’ recover nil %t panic nil %t", 84 params.name, 85 stackLocExp == nil, 86 panicLocExp == nil, 87 ) 88 } 89 90 // recoveryIndex should match 91 if actLine, expLine := stackAct.Frames()[stackIndexAct].Loc().FuncLine(), stackLocExp.FuncLine(); actLine != expLine { 92 t.Errorf("FAIL test ‘%s’ Indices() recovery index: %d:\n%s\n%s", 93 params.name, stackIndexAct, 94 actLine, expLine, 95 ) 96 } 97 98 // panicIndex should match 99 if actLine, expLine := stackAct.Frames()[panicIndexAct].Loc().FuncLine(), panicLocExp.FuncLine(); actLine != expLine { 100 t.Errorf("FAIL test ‘%s’ Indices() panic index %d:\n%s\n%s", 101 params.name, panicIndexAct, 102 actLine, expLine, 103 ) 104 } 105 } 106 } 107 108 // noPanic returns a deferred stack trace generated without a panic 109 func noPanic() (stack pruntime.Stack, panicLoc, recoverLoc *pruntime.CodeLocation) { 110 defer noPanicDefer(&stack) 111 112 return 113 } 114 115 // noPanicDefer is defereed function for noPanic 116 func noPanicDefer(stackp *pruntime.Stack) { 117 *stackp = pruntime.NewStack(0) 118 } 119 120 // invokePanic returns a deferred stack trace generated by panic(1) 121 func invokePanic() (stack pruntime.Stack, panicLoc, stackLoc *pruntime.CodeLocation) { 122 var one = 1 123 defer invokePanicDefer(one, &stack, &stackLoc) 124 125 for panicLoc = pruntime.NewCodeLocation(0); ; panic(one) { 126 } 127 } 128 129 // invokePanicDefer is defer function for panicOne 130 func invokePanicDefer(one int, stackp *pruntime.Stack, stackLoc **pruntime.CodeLocation) { 131 var recoverValue = recover() 132 if recoverValue != one { 133 panic(fmt.Errorf("bad recover value: %T “%[1]v” exp: %d", 134 recoverValue, 135 one, 136 )) 137 } 138 var s, loc = pruntime.NewStack(0), pruntime.NewCodeLocation(0) 139 *stackp = s 140 *stackLoc = loc 141 } 142 143 // panicFunction recovers a panic using [parl.RecoverErr] 144 // - panicLine is the exact code line of the panic 145 // - err is the error value produced by [parl.RecoverErr] 146 func nilPointerDereference() (stack pruntime.Stack, panicLoc, stackLoc *pruntime.CodeLocation) { 147 // runtime.errorString “runtime error: invalid memory address or nil pointer dereference” 148 // - runtime.errorString implements error 149 // - only methods are Error() and RuntimeError() 150 var message = "runtime error: invalid memory address or nil pointer dereference" 151 defer nilPointerDereferenceDefer(message, &stack, &stackLoc) 152 153 // nil pointer dereference panic 154 for panicLoc = pruntime.NewCodeLocation(0); ; _ = *(*int)(nil) { 155 } 156 } 157 158 // nilPointerDereferenceDefer is defer function for panicNilPointer 159 func nilPointerDereferenceDefer(message string, stackp *pruntime.Stack, stackLoc **pruntime.CodeLocation) { 160 var recoverValue = recover() 161 var isOk bool 162 if err, ok := recoverValue.(error); ok { 163 var runtimeError runtime.Error 164 if errors.As(err, &runtimeError) { 165 isOk = err.Error() == message 166 } 167 } 168 if !isOk { 169 panic(fmt.Errorf("bad recover value: %T “%[1]v” exp err message: “%s”", 170 recoverValue, 171 message, 172 )) 173 } 174 var s, loc = pruntime.NewStack(0), pruntime.NewCodeLocation(0) 175 *stackp = s 176 *stackLoc = loc 177 } 178 179 // panicFunction recovers a panic using [parl.RecoverErr] 180 // - panicLine is the exact code line of the panic 181 // - err is the error value produced by [parl.RecoverErr] 182 func slicePanic() (stack pruntime.Stack, panicLoc, stackLoc *pruntime.CodeLocation) { 183 // runtime.errorString “runtime error: invalid memory address or nil pointer dereference” 184 // - runtime.errorString implements error 185 // - only methods are Error() and RuntimeError() 186 var message = "runtime error: index out of range [0] with length 0" 187 defer slicePanicDefer(message, &stack, &stackLoc) 188 189 // nil pointer dereference panic 190 var slice = make([]byte, 0) 191 for panicLoc = pruntime.NewCodeLocation(0); ; _ = slice[0] { 192 } 193 } 194 195 // slicePanicDefer is defer function for slicePanic 196 func slicePanicDefer(message string, stackp *pruntime.Stack, stackLoc **pruntime.CodeLocation) { 197 var recoverValue = recover() 198 var isOk bool 199 if err, ok := recoverValue.(error); ok { 200 var runtimeError runtime.Error 201 if errors.As(err, &runtimeError) { 202 isOk = err.Error() == message 203 } 204 } 205 if !isOk { 206 panic(fmt.Errorf("bad recover value: %T “%[1]v” exp err message: “%s”", 207 recoverValue, 208 message, 209 )) 210 } 211 var s, loc = pruntime.NewStack(0), pruntime.NewCodeLocation(0) 212 *stackp = s 213 *stackLoc = loc 214 }