Documentation
¶
Overview ¶
Package parleak reports the goroutines a single test leaks, including tests that run under t.Parallel.
A test starts its goroutines through a Group instead of the go keyword. New binds a Group to a test and registers a t.Cleanup that cancels the group's context, waits up to a timeout for the tracked goroutines to return, and reports any still running. A Group reports only the goroutines started through it, so the check runs per test even under t.Parallel.
Example (Leak) ¶
Example_leak shows what parleak reports when a tracked goroutine ignores cancellation and is still running when the test's cleanup checks it.
package main
import (
"context"
"fmt"
"strings"
"time"
"github.com/slepp/parleak"
)
// fakeTB stands in for *testing.T. An example function does not receive a
// *testing.T, so this records failures instead of failing, which lets the
// example print what parleak reported. Real tests pass their own *testing.T to
// parleak.New; see the New documentation for that form.
type fakeTB struct {
errors []string
cleanups []func()
}
func (f *fakeTB) Errorf(format string, args ...any) {
f.errors = append(f.errors, fmt.Sprintf(format, args...))
}
func (f *fakeTB) Cleanup(fn func()) { f.cleanups = append(f.cleanups, fn) }
func (f *fakeTB) Helper() {}
// runCleanups runs the registered cleanups in LIFO order, as testing does when
// a test ends.
func (f *fakeTB) runCleanups() {
for i := len(f.cleanups) - 1; i >= 0; i-- {
f.cleanups[i]()
}
}
// Example_leak shows what parleak reports when a tracked goroutine ignores
// cancellation and is still running when the test's cleanup checks it.
func main() {
release := make(chan struct{})
defer close(release)
t := &fakeTB{}
g := parleak.New(t, parleak.WithTimeout(10*time.Millisecond))
g.Go("poller", func(ctx context.Context) {
<-release // never checks ctx.Done, so it outlives the test
})
t.runCleanups() // testing runs the registered cleanup when a test ends
// The report also names the launch site (file:line from runtime.Caller) and
// the likely cause; only the first line is stable enough to print here.
fmt.Println(strings.SplitN(t.errors[0], "\n", 2)[0])
}
Output: parleak: goroutine "poller" leaked: still running 10ms after cleanup cancelled the context
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Group ¶
type Group struct {
// contains filtered or unexported fields
}
Group tracks the goroutines a single test starts through Go and, when the test's cleanup runs, reports any still running after the group's context is cancelled and a timeout elapses. Create a Group with New.
A Group is safe for concurrent use, and Go may be called from any goroutine while the test runs. A goroutine started after the cleanup check has begun is not included in leak detection.
func New ¶
New returns a Group bound to t and registers a t.Cleanup that runs the leak check when the test ends. With a real *testing.T the cleanup runs automatically, so the check is not a separate call to remember. Options such as WithTimeout configure the check.
func TestWorker(t *testing.T) {
t.Parallel()
g := parleak.New(t)
g.Go("poller", func(ctx context.Context) {
poll(ctx) // must return when ctx is done
})
// exercise the system under test; the registered cleanup then cancels
// the context and reports "poller" if it is still running.
}
func (*Group) Context ¶
Context returns the Group's context. It's cancelled when the test's cleanup runs. Pass it to the system under test so cancellation reaches code that a tracked goroutine depends on.
func (*Group) Go ¶
Go starts fn in a new goroutine tracked by the Group. The label names the goroutine in a failure message, and Go records its own call site as the launch site.
fn receives the Group's context and is expected to return when that context is done. If it does not, the cleanup registered by New reports it as a leak.
A panic in fn is recovered rather than crashing the process. If it happens before the group's leak check records its verdict, it fails the owning test. A later panic can no longer fail the test and is written to standard error.
type Option ¶
type Option func(*config)
Option configures a Group at construction time.
func WithStackDump ¶
func WithStackDump() Option
WithStackDump appends a process-wide goroutine dump to the leak reports, once per check. The dump is the output of runtime.Stack, stripped of parleak's own reporting frame and indented; it contains goroutines from other tests running in parallel and is not trimmed to the leaked ones. Every goroutine parleak started ends its block with "created by github.com/slepp/parleak.(*Group).Go", so search for that string to find them all; within a block the top frame is where the goroutine is blocked, and the function passed to Go sits directly above the parleak.(*Group).Go.func1 wrapper frame. The label does not appear in the dump.
func WithTimeout ¶
WithTimeout sets how long a Group waits, after cancelling its context, for a tracked goroutine to return before reporting it as leaked. The default is one second. A non-positive duration means don't wait at all: a goroutine that hasn't already returned is reported immediately.