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  }