Source file src/net/http/internal/http3/main_test.go

     1  // Copyright 2026 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 http3_test
     6  
     7  import (
     8  	"fmt"
     9  	"os"
    10  	"runtime"
    11  	"slices"
    12  	"strings"
    13  	"testing"
    14  	"time"
    15  )
    16  
    17  func TestMain(m *testing.M) {
    18  	v := m.Run()
    19  	if v == 0 && goroutineLeaked() {
    20  		os.Exit(1)
    21  	}
    22  	os.Exit(v)
    23  }
    24  
    25  func runningBenchmarks() bool {
    26  	for i, arg := range os.Args {
    27  		if strings.HasPrefix(arg, "-test.bench=") && !strings.HasSuffix(arg, "=") {
    28  			return true
    29  		}
    30  		if arg == "-test.bench" && i < len(os.Args)-1 && os.Args[i+1] != "" {
    31  			return true
    32  		}
    33  	}
    34  	return false
    35  }
    36  
    37  func interestingGoroutines() (gs []string) {
    38  	buf := make([]byte, 2<<20)
    39  	buf = buf[:runtime.Stack(buf, true)]
    40  	for g := range strings.SplitSeq(string(buf), "\n\n") {
    41  		_, stack, _ := strings.Cut(g, "\n")
    42  		stack = strings.TrimSpace(stack)
    43  		if stack == "" ||
    44  			strings.Contains(stack, "testing.(*M).before.func1") ||
    45  			strings.Contains(stack, "os/signal.signal_recv") ||
    46  			strings.Contains(stack, "created by net.startServer") ||
    47  			strings.Contains(stack, "created by testing.RunTests") ||
    48  			strings.Contains(stack, "closeWriteAndWait") ||
    49  			strings.Contains(stack, "testing.Main(") ||
    50  			// These only show up with GOTRACEBACK=2; Issue 5005 (comment 28)
    51  			strings.Contains(stack, "runtime.goexit") ||
    52  			strings.Contains(stack, "created by runtime.gc") ||
    53  			strings.Contains(stack, "interestingGoroutines") ||
    54  			strings.Contains(stack, "runtime.MHeap_Scavenger") {
    55  			continue
    56  		}
    57  		gs = append(gs, stack)
    58  	}
    59  	slices.Sort(gs)
    60  	return
    61  }
    62  
    63  // Verify the other tests didn't leave any goroutines running.
    64  func goroutineLeaked() bool {
    65  	if testing.Short() || runningBenchmarks() {
    66  		// Don't worry about goroutine leaks in -short mode or in
    67  		// benchmark mode. Too distracting when there are false positives.
    68  		return false
    69  	}
    70  
    71  	var stackCount map[string]int
    72  	for range 5 {
    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  			return false
    82  		}
    83  		// Wait for goroutines to schedule and die off:
    84  		time.Sleep(100 * time.Millisecond)
    85  	}
    86  	fmt.Fprintf(os.Stderr, "Too many goroutines running after net/http test(s).\n")
    87  	for stack, count := range stackCount {
    88  		fmt.Fprintf(os.Stderr, "%d instances of:\n%s\n", count, stack)
    89  	}
    90  	return true
    91  }
    92  

View as plain text