github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/gmhttp/main_test.go (about) 1 // Copyright 2013 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package gmhttp_test 6 7 import ( 8 "fmt" 9 "io" 10 "log" 11 "os" 12 "runtime" 13 "sort" 14 "strings" 15 "testing" 16 "time" 17 18 http "github.com/hxx258456/ccgo/gmhttp" 19 ) 20 21 var quietLog = log.New(io.Discard, "", 0) 22 23 func TestMain(m *testing.M) { 24 v := m.Run() 25 if v == 0 && goroutineLeaked() { 26 os.Exit(1) 27 } 28 os.Exit(v) 29 } 30 31 func interestingGoroutines() (gs []string) { 32 buf := make([]byte, 2<<20) 33 buf = buf[:runtime.Stack(buf, true)] 34 for _, g := range strings.Split(string(buf), "\n\n") { 35 sl := strings.SplitN(g, "\n", 2) 36 if len(sl) != 2 { 37 continue 38 } 39 stack := strings.TrimSpace(sl[1]) 40 if stack == "" || 41 strings.Contains(stack, "testing.(*M).before.func1") || 42 strings.Contains(stack, "os/signal.signal_recv") || 43 strings.Contains(stack, "created by net.startServer") || 44 strings.Contains(stack, "created by testing.RunTests") || 45 strings.Contains(stack, "closeWriteAndWait") || 46 strings.Contains(stack, "testing.Main(") || 47 // These only show up with GOTRACEBACK=2; Issue 5005 (comment 28) 48 strings.Contains(stack, "runtime.goexit") || 49 strings.Contains(stack, "created by runtime.gc") || 50 strings.Contains(stack, "gmgo/gmhttp_test.interestingGoroutines") || 51 strings.Contains(stack, "runtime.MHeap_Scavenger") { 52 continue 53 } 54 gs = append(gs, stack) 55 } 56 sort.Strings(gs) 57 return 58 } 59 60 // Verify the other tests didn't leave any goroutines running. 61 func goroutineLeaked() bool { 62 if testing.Short() || runningBenchmarks() { 63 // Don't worry about goroutine leaks in -short mode or in 64 // benchmark mode. Too distracting when there are false positives. 65 return false 66 } 67 68 var stackCount map[string]int 69 for i := 0; i < 5; i++ { 70 n := 0 71 stackCount = make(map[string]int) 72 gs := interestingGoroutines() 73 for _, g := range gs { 74 stackCount[g]++ 75 n++ 76 } 77 if n == 0 { 78 return false 79 } 80 // Wait for goroutines to schedule and die off: 81 time.Sleep(100 * time.Millisecond) 82 } 83 fmt.Fprintf(os.Stderr, "Too many goroutines running after github.com/hxx258456/ccgo/gmhttp test(s).\n") 84 for stack, count := range stackCount { 85 fmt.Fprintf(os.Stderr, "%d instances of:\n%s\n", count, stack) 86 } 87 return true 88 } 89 90 // setParallel marks t as a parallel test if we're in short mode 91 // (all.bash), but as a serial test otherwise. Using t.Parallel isn't 92 // compatible with the afterTest func in non-short mode. 93 func setParallel(t *testing.T) { 94 if strings.Contains(t.Name(), "HTTP2") { 95 http.CondSkipHTTP2(t) 96 } 97 if testing.Short() { 98 t.Parallel() 99 } 100 } 101 102 func runningBenchmarks() bool { 103 for i, arg := range os.Args { 104 if strings.HasPrefix(arg, "-test.bench=") && !strings.HasSuffix(arg, "=") { 105 return true 106 } 107 if arg == "-test.bench" && i < len(os.Args)-1 && os.Args[i+1] != "" { 108 return true 109 } 110 } 111 return false 112 } 113 114 func afterTest(t testing.TB) { 115 http.DefaultTransport.(*http.Transport).CloseIdleConnections() 116 if testing.Short() { 117 return 118 } 119 var bad string 120 badSubstring := map[string]string{ 121 ").readLoop(": "a Transport", 122 ").writeLoop(": "a Transport", 123 "created by github.com/hxx258456/ccgo/gmhttp/httptest.(*Server).Start": "an httptest.Server", 124 "timeoutHandler": "a TimeoutHandler", 125 "net.(*netFD).connect(": "a timing out dial", 126 ").noteClientGone(": "a closenotifier sender", 127 } 128 var stacks string 129 for i := 0; i < 10; i++ { 130 bad = "" 131 stacks = strings.Join(interestingGoroutines(), "\n\n") 132 for substr, what := range badSubstring { 133 if strings.Contains(stacks, substr) { 134 bad = what 135 } 136 } 137 if bad == "" { 138 return 139 } 140 // Bad stuff found, but goroutines might just still be 141 // shutting down, so give it some time. 142 time.Sleep(250 * time.Millisecond) 143 } 144 t.Errorf("Test appears to have leaked %s:\n%s", bad, stacks) 145 } 146 147 // waitCondition reports whether fn eventually returned true, 148 // checking immediately and then every checkEvery amount, 149 // until waitFor has elapsed, at which point it returns false. 150 func waitCondition(waitFor, checkEvery time.Duration, fn func() bool) bool { 151 deadline := time.Now().Add(waitFor) 152 for time.Now().Before(deadline) { 153 if fn() { 154 return true 155 } 156 time.Sleep(checkEvery) 157 } 158 return false 159 } 160 161 // waitErrCondition is like waitCondition but with errors instead of bools. 162 func waitErrCondition(waitFor, checkEvery time.Duration, fn func() error) error { 163 deadline := time.Now().Add(waitFor) 164 var err error 165 for time.Now().Before(deadline) { 166 if err = fn(); err == nil { 167 return nil 168 } 169 time.Sleep(checkEvery) 170 } 171 return err 172 }