github.com/onsi/gomega@v1.32.0/gleak/ignoring_top_function.go (about) 1 package gleak 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/onsi/gomega/format" 8 "github.com/onsi/gomega/types" 9 ) 10 11 // IgnoringTopFunction succeeds if the topmost function in the backtrace of an 12 // actual goroutine has the specified function name, and optionally the actual 13 // goroutine has the specified goroutine state. 14 // 15 // The expected top function name topfn is either in the form of 16 // "topfunction-name", "topfunction-name...", or "topfunction-name [state]". 17 // 18 // An ellipsis "..." after a topfunction-name matches any goroutine's top 19 // function name if topfunction-name is a prefix and the goroutine's top 20 // function name is at least one level deeper. For instance, "foo.bar..." 21 // matches "foo.bar.baz", but doesn't match "foo.bar". 22 // 23 // If the optional expected state is specified, then a goroutine's state needs 24 // to start with this expected state text. For instance, "foo.bar [running]" 25 // matches a goroutine where the name of the top function is "foo.bar" and the 26 // goroutine's state starts with "running". 27 func IgnoringTopFunction(topfname string) types.GomegaMatcher { 28 if brIndex := strings.Index(topfname, "["); brIndex >= 0 { 29 expectedState := strings.Trim(topfname[brIndex:], "[]") 30 expectedTopFunction := strings.Trim(topfname[:brIndex], " ") 31 return &ignoringTopFunctionMatcher{ 32 expectedTopFunction: expectedTopFunction, 33 expectedState: expectedState, 34 } 35 } 36 if strings.HasSuffix(topfname, "...") { 37 expectedTopFunction := topfname[:len(topfname)-3+1] // ...one trailing dot still expected 38 return &ignoringTopFunctionMatcher{ 39 expectedTopFunction: expectedTopFunction, 40 matchPrefix: true, 41 } 42 } 43 return &ignoringTopFunctionMatcher{ 44 expectedTopFunction: topfname, 45 } 46 } 47 48 type ignoringTopFunctionMatcher struct { 49 expectedTopFunction string 50 expectedState string 51 matchPrefix bool 52 } 53 54 // Match succeeds if an actual goroutine's top function in the backtrace matches 55 // the specified function name or function name prefix, or name and goroutine 56 // state. 57 func (matcher *ignoringTopFunctionMatcher) Match(actual interface{}) (success bool, err error) { 58 g, err := G(actual, "IgnoringTopFunction") 59 if err != nil { 60 return false, err 61 } 62 if matcher.matchPrefix { 63 return strings.HasPrefix(g.TopFunction, matcher.expectedTopFunction), nil 64 } 65 if g.TopFunction != matcher.expectedTopFunction { 66 return false, nil 67 } 68 if matcher.expectedState == "" { 69 return true, nil 70 } 71 return strings.HasPrefix(g.State, matcher.expectedState), nil 72 } 73 74 // FailureMessage returns a failure message if the actual goroutine doesn't have 75 // the specified function name/prefix (and optional state) at the top of the 76 // backtrace. 77 func (matcher *ignoringTopFunctionMatcher) FailureMessage(actual interface{}) (message string) { 78 return format.Message(actual, matcher.message()) 79 } 80 81 // NegatedFailureMessage returns a failure message if the actual goroutine has 82 // the specified function name/prefix (and optional state) at the top of the 83 // backtrace. 84 func (matcher *ignoringTopFunctionMatcher) NegatedFailureMessage(actual interface{}) (message string) { 85 return format.Message(actual, "not "+matcher.message()) 86 } 87 88 func (matcher *ignoringTopFunctionMatcher) message() string { 89 if matcher.matchPrefix { 90 return fmt.Sprintf("to have the prefix %q for its topmost function", matcher.expectedTopFunction) 91 } 92 if matcher.expectedState != "" { 93 return fmt.Sprintf("to have the topmost function %q and the state %q", 94 matcher.expectedTopFunction, matcher.expectedState) 95 } 96 return fmt.Sprintf("to have the topmost function %q", matcher.expectedTopFunction) 97 }