Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add DefaultIgnoreFunctionSet #127

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions leaks.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,23 @@ package goleak
import (
"errors"
"fmt"
"strings"

"go.uber.org/goleak/internal/stack"
)

// DefaultIgnoreFunctionSet can be set to a comma-separated list
// of functions that may leak and should be ignored by goleak.
// The registered set of functions will be then used similar to parameters
// passed to [IgnoreAnyFunction]: any goroutine leaks with the given function
// name(s) will be ignored by goleak.
// This is helpful for library or tool authors that "owns" the leaking
// goroutine and do not have explicit control over how the tests are run
// by their consumers.
// Unless you are in such a situation, you should be using [IgnoreAnyFunction],
// [IgnoreCurrent], or [IgnoreTopFunction] instead.
var DefaultIgnoreFunctionSet string
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Risk with making this an exported variable here is that libraries can't easily cooperate on setting this. It'll take one library to do goleak.DefaultIgnoreFunctionSet = "foo" to override other exceptions.


// TestingT is the minimal subset of testing.TB that we use.
type TestingT interface {
Error(...interface{})
Expand All @@ -50,15 +63,30 @@ func filterStacks(stacks []stack.Stack, skipID int, opts *opts) []stack.Stack {
return filtered
}

func parseDefaultIgnoreFunctions() []Option {
// parse DefaultIgnoreFunctionSet and add it to filters
funcs := strings.Split(DefaultIgnoreFunctionSet, ",")
opts := make([]Option, 0, len(funcs))
for _, f := range funcs {
if f != "" {
opts = append(opts, IgnoreAnyFunction(f))
}
}
return opts
}

// Find looks for extra goroutines, and returns a descriptive error if
// any are found.
func Find(options ...Option) error {
cur := stack.Current().ID()

options = append(options, parseDefaultIgnoreFunctions()...)

opts := buildOpts(options...)
if opts.cleanup != nil {
return errors.New("Cleanup can only be passed to VerifyNone or VerifyTestMain")
}

var stacks []stack.Stack
retry := true
for i := 0; retry; i++ {
Expand Down
47 changes: 47 additions & 0 deletions leaks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,3 +201,50 @@ func TestVerifyParallel(t *testing.T) {
VerifyNone(t)
})
}

func TestDefaultIgnoreFunctionSet(t *testing.T) {
t.Run("single function", func(t *testing.T) {
done := make(chan struct{})
go func() {
<-done
}()
require.Error(t, Find(), "expected the leaking goroutine to get flagged")

DefaultIgnoreFunctionSet = "go.uber.org/goleak.TestDefaultIgnoreFunctionSet.func1.1"
assert.Equal(t, 1, len(parseDefaultIgnoreFunctions()))
assert.NoError(t, Find(), "expected the goroutine to get ignored after setting DefaultIgnoreFunctionSet")

DefaultIgnoreFunctionSet = ""
assert.Error(t, Find(), "expected the leaking goroutine to get flagged again after resetting DefaultIgnoreFunctionSet")

close(done)
assert.NoError(t, Find())
})

t.Run("many functions", func(t *testing.T) {
bg := startBlockedG()
bg2 := blockedG2()

require.Error(t, Find(), "expected the leaking goroutine to get flagged")

DefaultIgnoreFunctionSet = "go.uber.org/goleak.(*blockedG).block,go.uber.org/goleak.blockedG2.func1"

assert.Equal(t, 2, len(parseDefaultIgnoreFunctions()), "expected 2 filters to be added with DefaultIgnoreFunctionSet")
assert.NoError(t, Find(), "expected the goroutine to get ignored after setting DefaultIgnoreFunctionSet")

DefaultIgnoreFunctionSet = ""
assert.Error(t, Find(), "expected the leaking goroutine to get flagged again after resetting DefaultIgnoreFunctionSet")

bg.unblock()
close(bg2)
assert.NoError(t, Find())
})
}

func blockedG2() chan struct{} {
ch := make(chan struct{})
go func() {
<-ch
}()
return ch
}
Loading