diff --git a/CHANGELOG.md b/CHANGELOG.md index e589c8d..a2db308 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Built-in ignores now match function names more accurately. They will no longer ignore stacks because of file names that look similar to function names. +### Added +- Add an `IgnoreAnyFunction` option to ignore stack traces + that have the provided function anywhere in the stack. ## [1.2.1] ### Changed diff --git a/options.go b/options.go index 31b0c18..ad5e77b 100644 --- a/options.go +++ b/options.go @@ -83,6 +83,22 @@ func IgnoreTopFunction(f string) Option { }) } +// IgnoreAnyFunction ignores goroutines where the specified function +// is present anywhere in the stack. +// +// The function name must be fully qualified, e.g., +// +// go.uber.org/goleak.IgnoreAnyFunction +// +// For methods, the fully qualified form looks like: +// +// go.uber.org/goleak.(*MyType).MyMethod +func IgnoreAnyFunction(f string) Option { + return addFilter(func(s stack.Stack) bool { + return s.HasFunction(f) + }) +} + // Cleanup sets up a cleanup function that will be executed at the // end of the leak check. // When passed to [VerifyTestMain], the exit code passed to cleanupFunc diff --git a/options_test.go b/options_test.go index 11438da..bb2fe06 100644 --- a/options_test.go +++ b/options_test.go @@ -61,8 +61,31 @@ func TestOptionsFilters(t *testing.T) { require.Equal(t, 1, countUnfiltered(), "Expected blockedG goroutine to not match any filter") // If we add an extra filter to ignore blockTill, it shouldn't match. - opts = buildOpts(IgnoreTopFunction("go.uber.org/goleak.(*blockedG).run")) + opts = buildOpts(IgnoreTopFunction("go.uber.org/goleak.(*blockedG).block")) require.Zero(t, countUnfiltered(), "blockedG should be filtered out. running: %v", stack.All()) + + // If we ignore startBlockedG, that should not ignore the blockedG goroutine + // because startBlockedG should be the "created by" function in the stack. + opts = buildOpts(IgnoreAnyFunction("go.uber.org/goleak.startBlockedG")) + require.Equal(t, 1, countUnfiltered(), + "startBlockedG should not be filtered out. running: %v", stack.All()) +} + +func TestOptionsIgnoreAnyFunction(t *testing.T) { + cur := stack.Current() + opts := buildOpts(IgnoreAnyFunction("go.uber.org/goleak.(*blockedG).run")) + + for _, s := range stack.All() { + if s.ID() == cur.ID() { + continue + } + + if opts.filter(s) { + continue + } + + t.Errorf("Unexpected goroutine: %v", s) + } } func TestBuildOptions(t *testing.T) { diff --git a/utils_test.go b/utils_test.go index 7d6b0f1..5504774 100644 --- a/utils_test.go +++ b/utils_test.go @@ -45,6 +45,10 @@ func startBlockedG() *blockedG { func (bg *blockedG) run() { close(bg.started) + bg.block() +} + +func (bg *blockedG) block() { <-bg.wait }