github.com/newrelic/go-agent@v3.26.0+incompatible/_integrations/nrpkgerrors/nrkpgerrors_test.go (about) 1 // Copyright 2020 New Relic Corporation. All rights reserved. 2 // SPDX-License-Identifier: Apache-2.0 3 4 package nrpkgerrors 5 6 import ( 7 "runtime" 8 "strings" 9 "testing" 10 11 newrelic "github.com/newrelic/go-agent" 12 "github.com/pkg/errors" 13 ) 14 15 func topFrameFunction(stack []uintptr) string { 16 var frame runtime.Frame 17 frames := runtime.CallersFrames(stack) 18 if nil != frames { 19 frame, _ = frames.Next() 20 } 21 return frame.Function 22 } 23 24 type basicError struct{} 25 26 func (e basicError) Error() string { return "something went wrong" } 27 28 func alpha(e error) error { return errors.WithStack(e) } 29 func beta(e error) error { return errors.WithStack(e) } 30 func gamma(e error) error { return errors.WithStack(e) } 31 32 func theta(e error) error { return errors.WithMessage(e, "theta") } 33 34 func TestWrappedStackTrace(t *testing.T) { 35 testcases := []struct { 36 Error error 37 ExpectTopFrame string 38 }{ 39 {Error: basicError{}, ExpectTopFrame: ""}, 40 {Error: alpha(basicError{}), ExpectTopFrame: "alpha"}, 41 {Error: alpha(beta(gamma(basicError{}))), ExpectTopFrame: "gamma"}, 42 {Error: alpha(theta(basicError{})), ExpectTopFrame: "alpha"}, 43 {Error: alpha(theta(beta(basicError{}))), ExpectTopFrame: "beta"}, 44 {Error: alpha(theta(beta(theta(basicError{})))), ExpectTopFrame: "beta"}, 45 {Error: theta(basicError{}), ExpectTopFrame: ""}, 46 } 47 48 for idx, tc := range testcases { 49 e := Wrap(tc.Error) 50 st := e.(newrelic.StackTracer).StackTrace() 51 fn := topFrameFunction(st) 52 if !strings.Contains(fn, tc.ExpectTopFrame) { 53 t.Errorf("testcase %d: expected %s got %s", 54 idx, tc.ExpectTopFrame, fn) 55 } 56 } 57 } 58 59 type withClass struct{ class string } 60 61 func errorWithClass(class string) error { return withClass{class: class} } 62 63 func (e withClass) Error() string { return "something went wrong" } 64 func (e withClass) ErrorClass() string { return e.class } 65 66 type classAndCause struct { 67 cause error 68 class string 69 } 70 71 func wrapWithClass(e error, class string) error { return classAndCause{cause: e, class: class} } 72 73 func (e classAndCause) Error() string { return e.cause.Error() } 74 func (e classAndCause) Cause() error { return e.cause } 75 func (e classAndCause) ErrorClass() string { return e.class } 76 77 func TestWrappedErrorClass(t *testing.T) { 78 // First choice is any ErrorClass of the immediate error. 79 // Second choice is any ErrorClass of the error's cause. 80 // Final choice is the reflect type of the error's cause. 81 testcases := []struct { 82 Error error 83 ExpectClass string 84 }{ 85 {Error: basicError{}, ExpectClass: "nrpkgerrors.basicError"}, 86 {Error: errorWithClass("zap"), ExpectClass: "zap"}, 87 {Error: wrapWithClass(errorWithClass("zap"), "zip"), ExpectClass: "zip"}, 88 {Error: theta(wrapWithClass(errorWithClass("zap"), "zip")), ExpectClass: "zap"}, 89 {Error: alpha(basicError{}), ExpectClass: "nrpkgerrors.basicError"}, 90 {Error: wrapWithClass(basicError{}, "zip"), ExpectClass: "zip"}, 91 {Error: alpha(wrapWithClass(basicError{}, "zip")), ExpectClass: "nrpkgerrors.basicError"}, 92 } 93 94 for idx, tc := range testcases { 95 e := Wrap(tc.Error) 96 class := e.(newrelic.ErrorClasser).ErrorClass() 97 if class != tc.ExpectClass { 98 t.Errorf("testcase %d: expected %s got %s", 99 idx, tc.ExpectClass, class) 100 } 101 } 102 }