| // Copyright 2018 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package stacktest | |
| import ( | |
| "testing" | |
| "time" | |
| "golang.org/x/tools/internal/jsonrpc2/stack" | |
| ) | |
| // this is only needed to support pre 1.14 when testing.TB did not have Cleanup | |
| type withCleanup interface { | |
| Cleanup(func()) | |
| } | |
| // the maximum amount of time to wait for goroutines to clean themselves up. | |
| const maxWait = time.Second | |
| // NoLeak checks that a test (or benchmark) does not leak any goroutines. | |
| func NoLeak(t testing.TB) { | |
| c, ok := t.(withCleanup) | |
| if !ok { | |
| return | |
| } | |
| before := stack.Capture() | |
| c.Cleanup(func() { | |
| var delta stack.Delta | |
| start := time.Now() | |
| delay := time.Millisecond | |
| for { | |
| after := stack.Capture() | |
| delta = stack.Diff(before, after) | |
| if len(delta.After) == 0 { | |
| // no leaks | |
| return | |
| } | |
| if time.Since(start) > maxWait { | |
| break | |
| } | |
| time.Sleep(delay) | |
| delay *= 2 | |
| } | |
| // it's been long enough, and leaks are still present | |
| summary := stack.Summarize(delta.After) | |
| t.Errorf("goroutine leak detected:\n%+v", summary) | |
| }) | |
| } |