github.com/newrelic/go-agent@v3.26.0+incompatible/internal_errors_stacktrace_test.go (about) 1 // Copyright 2020 New Relic Corporation. All rights reserved. 2 // SPDX-License-Identifier: Apache-2.0 3 4 // +build go1.7 5 6 package newrelic 7 8 import ( 9 "runtime" 10 "strings" 11 "testing" 12 ) 13 14 // The use of runtime.CallersFrames requires Go 1.7+. 15 16 func topFrameFunction(stack []uintptr) string { 17 var frame runtime.Frame 18 frames := runtime.CallersFrames(stack) 19 if nil != frames { 20 frame, _ = frames.Next() 21 } 22 return frame.Function 23 } 24 25 type withStackAndCause struct { 26 cause error 27 stack []uintptr 28 } 29 30 type withStack struct { 31 stack []uintptr 32 } 33 34 func (e withStackAndCause) Error() string { return e.cause.Error() } 35 func (e withStackAndCause) StackTrace() []uintptr { return e.stack } 36 func (e withStackAndCause) Unwrap() error { return e.cause } 37 38 func (e withStack) Error() string { return "something went wrong" } 39 func (e withStack) StackTrace() []uintptr { return e.stack } 40 41 func generateStack() []uintptr { 42 skip := 2 // skip runtime.Callers and this function. 43 callers := make([]uintptr, 20) 44 written := runtime.Callers(skip, callers) 45 return callers[:written] 46 } 47 48 func alpha() []uintptr { return generateStack() } 49 func beta() []uintptr { return generateStack() } 50 51 func TestStackTrace(t *testing.T) { 52 // First choice is any StackTrace() of the immediate error. 53 // Second choice is any StackTrace() of the error's cause. 54 // Final choice is stack trace of the current location. 55 testcases := []struct { 56 Error error 57 ExpectTopFrame string 58 }{ 59 {Error: basicError{}, ExpectTopFrame: "internal.GetStackTrace"}, 60 {Error: withStack{stack: alpha()}, ExpectTopFrame: "alpha"}, 61 {Error: withStack{stack: nil}, ExpectTopFrame: "internal.GetStackTrace"}, 62 {Error: withStackAndCause{stack: alpha(), cause: basicError{}}, ExpectTopFrame: "alpha"}, 63 {Error: withStackAndCause{stack: nil, cause: withStack{stack: beta()}}, ExpectTopFrame: "beta"}, 64 {Error: withStackAndCause{stack: nil, cause: withStack{stack: nil}}, ExpectTopFrame: "internal.GetStackTrace"}, 65 } 66 67 for idx, tc := range testcases { 68 data, err := errDataFromError(tc.Error) 69 if err != nil { 70 t.Errorf("testcase %d: got error: %v", idx, err) 71 continue 72 } 73 fn := topFrameFunction(data.Stack) 74 if !strings.Contains(fn, tc.ExpectTopFrame) { 75 t.Errorf("testcase %d: expected %s got %s", 76 idx, tc.ExpectTopFrame, fn) 77 } 78 } 79 }