1package dns
2
3import (
4	"fmt"
5	"os"
6	"runtime"
7	"sort"
8	"strings"
9	"testing"
10	"time"
11)
12
13// copied from net/http/main_test.go
14
15func interestingGoroutines() (gs []string) {
16	buf := make([]byte, 2<<20)
17	buf = buf[:runtime.Stack(buf, true)]
18	for _, g := range strings.Split(string(buf), "\n\n") {
19		sl := strings.SplitN(g, "\n", 2)
20		if len(sl) != 2 {
21			continue
22		}
23		stack := strings.TrimSpace(sl[1])
24		if stack == "" ||
25			strings.Contains(stack, "testing.(*M).before.func1") ||
26			strings.Contains(stack, "os/signal.signal_recv") ||
27			strings.Contains(stack, "created by net.startServer") ||
28			strings.Contains(stack, "created by testing.RunTests") ||
29			strings.Contains(stack, "closeWriteAndWait") ||
30			strings.Contains(stack, "testing.Main(") ||
31			strings.Contains(stack, "testing.(*T).Run(") ||
32			// These only show up with GOTRACEBACK=2; Issue 5005 (comment 28)
33			strings.Contains(stack, "runtime.goexit") ||
34			strings.Contains(stack, "created by runtime.gc") ||
35			strings.Contains(stack, "dns.interestingGoroutines") ||
36			strings.Contains(stack, "runtime.MHeap_Scavenger") {
37			continue
38		}
39		gs = append(gs, stack)
40	}
41	sort.Strings(gs)
42	return
43}
44
45func goroutineLeaked() error {
46	if testing.Short() {
47		// Don't worry about goroutine leaks in -short mode or in
48		// benchmark mode. Too distracting when there are false positives.
49		return nil
50	}
51
52	var stackCount map[string]int
53	for i := 0; i < 5; i++ {
54		n := 0
55		stackCount = make(map[string]int)
56		gs := interestingGoroutines()
57		for _, g := range gs {
58			stackCount[g]++
59			n++
60		}
61		if n == 0 {
62			return nil
63		}
64		// Wait for goroutines to schedule and die off:
65		time.Sleep(100 * time.Millisecond)
66	}
67	for stack, count := range stackCount {
68		fmt.Fprintf(os.Stderr, "%d instances of:\n%s\n", count, stack)
69	}
70	return fmt.Errorf("too many goroutines running after dns test(s)")
71}
72