go.dedis.ch/onet/v4@v4.0.0-pre1/log/testutil.go (about) 1 package log 2 3 import ( 4 "fmt" 5 "runtime" 6 "runtime/debug" 7 "sort" 8 "strings" 9 "testing" 10 "time" 11 ) 12 13 func interestingGoroutines() (gs []string) { 14 buf := make([]byte, 2<<20) 15 buf = buf[:runtime.Stack(buf, true)] 16 for _, g := range strings.Split(string(buf), "\n\n") { 17 sl := strings.SplitN(g, "\n", 2) 18 if len(sl) != 2 { 19 continue 20 } 21 stack := strings.TrimSpace(sl[1]) 22 if stack == "" || 23 strings.Contains(stack, "(*LocalTest).CloseAll") || 24 strings.Contains(stack, "created by testing.RunTests") || 25 strings.Contains(stack, "testing.RunTests(") || 26 strings.Contains(stack, "testing.(*T).Run(") || 27 strings.Contains(stack, "testing.Main(") || 28 strings.Contains(stack, "runtime.goexit") || 29 strings.Contains(stack, "interestingGoroutines") || 30 strings.Contains(stack, "created by runtime.gc") || 31 strings.Contains(stack, "runtime.MHeap_Scavenger") || 32 strings.Contains(stack, "graceful") || 33 strings.Contains(stack, "sigqueue") || 34 strings.Contains(stack, "log.MainTest") || 35 matchesUserUninterestingGoroutine(stack) { 36 continue 37 } 38 39 gs = append(gs, stack) 40 } 41 sort.Strings(gs) 42 return 43 } 44 45 var userUninterestingGoroutines []string 46 47 // AddUserUninterestingGoroutine can be called when the environment of some 48 // specific tests leaks goroutines unknown to interestingGoroutines(). 49 // This function is not safe for concurrent execution. The caller should add 50 // all of the desired exceptions before launching any goroutines. 51 func AddUserUninterestingGoroutine(newGr string) { 52 userUninterestingGoroutines = append(userUninterestingGoroutines, newGr) 53 } 54 55 func matchesUserUninterestingGoroutine(stack string) bool { 56 for _, gr := range userUninterestingGoroutines { 57 if strings.Contains(stack, gr) { 58 return true 59 } 60 } 61 62 return false 63 } 64 65 // AfterTest can be called to wait for leaking goroutines to finish. If 66 // they do not finish after a reasonable time (600ms) the test will fail. 67 // 68 // Inspired by https://golang.org/src/net/http/main_test.go 69 // and https://github.com/coreos/etcd/blob/master/pkg/testutil/leak.go 70 func AfterTest(t *testing.T) { 71 var stackCount map[string]int 72 for i := 0; i < 10; i++ { 73 n := 0 74 stackCount = make(map[string]int) 75 gs := interestingGoroutines() 76 for _, g := range gs { 77 stackCount[g]++ 78 n++ 79 } 80 if n == 0 { 81 break 82 } 83 // Wait for goroutines to schedule and die off: 84 time.Sleep(100 * time.Millisecond) 85 } 86 if len(stackCount) > 0 { 87 for stack, count := range stackCount { 88 if t != nil { 89 t.Logf("%d instances of:\n%s\n", count, stack) 90 } else { 91 Error(fmt.Sprintf("%d instances of:\n%s\n", count, stack)) 92 } 93 } 94 Print("Stack-trace of caller: ", Stack()) 95 if t != nil { 96 t.Fatalf("Test leaks %d goroutines.", len(stackCount)) 97 } else { 98 Fatal(fmt.Sprintf("Test leaks %d goroutines.", len(stackCount))) 99 } 100 } 101 } 102 103 // Stack converts []byte to string 104 func Stack() string { 105 return string(debug.Stack()) 106 }