MCPcopy
hub / github.com/tailscale/tailscale / ResourceCheck

Function ResourceCheck

tstest/resource.go:22–66  ·  view source on GitHub ↗

ResourceCheck takes a snapshot of the current goroutines and registers a cleanup on tb to verify that after the rest, all goroutines created by the test go away. (well, at least that the count matches. Maybe in the future it can look at specific routines). It panics if called from a parallel test.

(tb testing.TB)

Source from the content-addressed store, hash-verified

20//
21// It panics if called from a parallel test.
22func ResourceCheck(tb testing.TB) {
23 tb.Helper()
24
25 // Set an environment variable (anything at all) just for the
26 // side effect of tb.Setenv panicking if we're in a parallel test.
27 tb.Setenv("TS_CHECKING_RESOURCES", "1")
28
29 startN, startStacks := goroutines()
30 tb.Cleanup(func() {
31 if tb.Failed() {
32 // Test has failed - but this doesn't catch panics due to
33 // https://github.com/golang/go/issues/49929.
34 return
35 }
36 // Goroutines might be still exiting.
37 for range 300 {
38 if runtime.NumGoroutine() <= startN {
39 return
40 }
41 time.Sleep(10 * time.Millisecond)
42 }
43 endN, endStacks := goroutines()
44 if endN <= startN {
45 return
46 }
47
48 // Parse and print goroutines.
49 start := parseGoroutines(startStacks)
50 end := parseGoroutines(endStacks)
51 if testing.Verbose() {
52 tb.Logf("goroutines start:\n%s", printGoroutines(start))
53 tb.Logf("goroutines end:\n%s", printGoroutines(end))
54 }
55
56 // Print goroutine diff, omitting tstest.ResourceCheck goroutines.
57 self := func(g goroutine) bool { return bytes.Contains(g.stack, []byte("\ttailscale.com/tstest.goroutines+")) }
58 start.goroutines = slices.DeleteFunc(start.goroutines, self)
59 end.goroutines = slices.DeleteFunc(end.goroutines, self)
60 tb.Logf("goroutine diff (-start +end):\n%s", diffGoroutines(start, end))
61
62 // tb.Failed() above won't report on panics, so we shouldn't call Fatal
63 // here or we risk suppressing reporting of the panic.
64 tb.Errorf("goroutine count: expected %d, got %d\n", startN, endN)
65 })
66}
67
68func goroutines() (int, []byte) {
69 p := pprof.Lookup("goroutine")

Callers 15

TestDialBlocksFunction · 0.92
TestConnFunction · 0.92
TestLoopbackLocalAPIFunction · 0.92
TestFallbackTCPHandlerFunction · 0.92
TestUDPConnFunction · 0.92
setupTwoClientTestFunction · 0.92
TestSelfDialFunction · 0.92
TestNewConnFunction · 0.92
TestPickDERPFallbackFunction · 0.92
TestDeviceStartStopFunction · 0.92
TestDiscokeyChangeFunction · 0.92
TestActiveDiscoveryFunction · 0.92

Calls 11

goroutinesFunction · 0.85
parseGoroutinesFunction · 0.85
printGoroutinesFunction · 0.85
diffGoroutinesFunction · 0.85
HelperMethod · 0.65
SetenvMethod · 0.65
CleanupMethod · 0.65
FailedMethod · 0.65
LogfMethod · 0.65
ErrorfMethod · 0.65
ContainsMethod · 0.45

Tested by 15

TestDialBlocksFunction · 0.74
TestConnFunction · 0.74
TestLoopbackLocalAPIFunction · 0.74
TestFallbackTCPHandlerFunction · 0.74
TestUDPConnFunction · 0.74
setupTwoClientTestFunction · 0.74
TestSelfDialFunction · 0.74
TestNewConnFunction · 0.74
TestPickDERPFallbackFunction · 0.74
TestDeviceStartStopFunction · 0.74
TestDiscokeyChangeFunction · 0.74
TestActiveDiscoveryFunction · 0.74

Used in the wild real call sites across dependent graphs

searching dependent graphs…