// When a test is skipped or fails, runtime.Goexit() is called which destroys the callstack. // This means the name of the test case is lost, so we need to grab a copy of pc before. func Report(t testing.TB) { // If the goroutine panics, Fatal()s, or Skip()s, the function name is at the 3rd callstack // layer. On success, its at 1st. Since it's hard to check which happened, just try both. pcs := make([]uintptr, 10) total := runtime.Callers(1, pcs) var name string for _, pc := range pcs[:total] { fn := runtime.FuncForPC(pc) fullName := fn.Name() if strings.HasPrefix(path.Ext(fullName), ".Test") { // Skip the leading . name = string([]byte(path.Ext(fullName))[1:]) break } } if name == "" { return } allCaseInfos.lock.Lock() defer allCaseInfos.lock.Unlock() allCaseInfos.Cases = append(allCaseInfos.Cases, &caseInfo{ Name: name, Passed: !t.Failed() && !t.Skipped(), Skipped: t.Skipped(), Fatal: t.Failed() && !strings.HasPrefix(name, "TestSoon"), }) }
// AfterTest snapshots the currently-running goroutines and returns a // function to be run at the end of tests to see whether any // goroutines leaked. func AfterTest(t testing.TB) func() { orig := interestingGoroutines() return func() { if t.Failed() { return } if r := recover(); r != nil { panic(r) } // Loop, waiting for goroutines to shut down. // Wait up to 5 seconds, but finish as quickly as possible. deadline := timeutil.Now().Add(5 * time.Second) for { var leaked []string for id, stack := range interestingGoroutines() { if _, ok := orig[id]; !ok { leaked = append(leaked, stack) } } if len(leaked) == 0 { return } if timeutil.Now().Before(deadline) { time.Sleep(50 * time.Millisecond) continue } sort.Strings(leaked) for _, g := range leaked { t.Errorf("Leaked goroutine: %v", g) } return } } }
// WithTestServer creates a new TestServer, runs the passed function, and then // verifies that no resources were leaked. func WithTestServer(t testing.TB, chanOpts *ChannelOpts, f func(*TestServer)) { chanOpts = chanOpts.Copy() runCount := chanOpts.RunCount if runCount < 1 { runCount = 1 } for i := 0; i < runCount; i++ { if t.Failed() { return } // Run without the relay, unless OnlyRelay was set. if !chanOpts.OnlyRelay { noRelayOpts := chanOpts.Copy() noRelayOpts.DisableRelay = true withServer(t, noRelayOpts, f) } // Run with the relay, unless the user has disabled it. if !chanOpts.DisableRelay { withServer(t, chanOpts.Copy(), f) } } }
// AfterTest snapshots the currently-running goroutines and returns a // function to be run at the end of tests to see whether any // goroutines leaked. func AfterTest(t testing.TB) func() { orig := map[string]bool{} for _, g := range interestingGoroutines() { orig[g] = true } return func() { if t.Failed() { return } // Loop, waiting for goroutines to shut down. // Wait up to 5 seconds, but finish as quickly as possible. deadline := timeutil.Now().Add(5 * time.Second) for { var leaked []string for _, g := range interestingGoroutines() { if !orig[g] { leaked = append(leaked, g) } } if len(leaked) == 0 { return } if timeutil.Now().Before(deadline) { time.Sleep(50 * time.Millisecond) continue } for _, g := range leaked { t.Errorf("Leaked goroutine: %v", g) } return } } }
// AfterTest should be called (generally with "defer leaktest.AfterTest(t)") // from each test which uses goroutines. This waits for all goroutines // on a blacklist to terminate and provides more precise error reporting // than TestMainWithLeakCheck alone. // If a previous test's check has already failed, this is a noop (to avoid // failing unrelated tests). func AfterTest(t testing.TB) { if atomic.LoadInt32(&hasFailed) > 0 { t.Log("prior leak detected, leaktest disabled") return } if r := recover(); r != nil { // Don't bother with leaktest if we're recovering from a panic. panic(r) } http.DefaultTransport.(*http.Transport).CloseIdleConnections() if testing.Short() || t.Failed() { // If a test has failed, chances are it hasn't shut down cleanly, so // stop checking for leaks in this run altogether. atomic.StoreInt32(&hasFailed, 1) return } if r := recover(); r != nil { panic(r) } var bad string badSubstring := map[string]string{ ").readLoop(": "a Transport", ").writeLoop(": "a Transport", "created by net/http/httptest.(*Server).Start": "an httptest.Server", "timeoutHandler": "a TimeoutHandler", "net.(*netFD).connect(": "a timing out dial", ").noteClientGone(": "a closenotifier sender", "created by net/rpc.NewClientWithCodec": "an rpc client", "created by net/rpc.(*Server.ServeCodec)": "an rpc server connection", "(*Store).Start": "a store", "(*Range).Send": "a range command", } var stacks string for i := 0; i < 8; i++ { bad = "" stacks = strings.Join(interestingGoroutines(), "\n\n") for substr, what := range badSubstring { if strings.Contains(stacks, substr) { bad = what } } if bad == "" { return } // Bad stuff found, but goroutines might just still be // shutting down, so give it some time. time.Sleep(10 * time.Millisecond) } atomic.StoreInt32(&hasFailed, 1) t.Errorf("Test appears to have leaked %s:\n%s", bad, stacks) }
// AfterTest should be called (generally with "defer leaktest.AfterTest(t)") // from each test which uses goroutines. This waits for all goroutines // on a blacklist to terminate and provides more precise error reporting // than TestMainWithLeakCheck alone. func AfterTest(t testing.TB) { http.DefaultTransport.(*http.Transport).CloseIdleConnections() if testing.Short() || t.Failed() { return } if r := recover(); r != nil { panic(r) } var bad string badSubstring := map[string]string{ ").readLoop(": "a Transport", ").writeLoop(": "a Transport", "created by net/http/httptest.(*Server).Start": "an httptest.Server", "timeoutHandler": "a TimeoutHandler", "net.(*netFD).connect(": "a timing out dial", ").noteClientGone(": "a closenotifier sender", "created by net/rpc.NewClientWithCodec": "an rpc client", "created by net/rpc.(*Server.ServeCodec)": "an rpc server connection", "(*Store).Start": "a store", "(*Range).AddCmd": "a range command", } var stacks string for i := 0; i < 8; i++ { bad = "" stacks = strings.Join(interestingGoroutines(), "\n\n") for substr, what := range badSubstring { if strings.Contains(stacks, substr) { bad = what } } if bad == "" { return } // Bad stuff found, but goroutines might just still be // shutting down, so give it some time. time.Sleep(10 * time.Millisecond) } t.Errorf("Test appears to have leaked %s:\n%s", bad, stacks) }
func (m *MockStats) assertCallEqual(t testing.TB, expected *MockCallStats, actual *MockCallStats) { // Revisit these assertions if we ever need to assert zero or many calls to // End. require.Equal(t, 1, expected.ended, "Expected call must assert exactly one call to End.") require.False( t, expected.succeeded <= 0 && len(expected.failedMsgs) == 0, "Expectation must indicate whether RPC should succeed or fail.", ) assert.Equal(t, expected.succeeded, actual.succeeded, "Unexpected number of successes.") assert.Equal(t, expected.failedMsgs, actual.failedMsgs, "Unexpected reasons for RPC failure.") assert.Equal(t, expected.ended, actual.ended, "Unexpected number of calls to End.") if expected.verifyPeer { assert.Equal(t, expected.peer, actual.peer, "Unexpected peer used for the call") } if t.Failed() { // The default testify output is often insufficient. t.Logf("\nExpected relayed stats were:\n\t%+v\nActual relayed stats were:\n\t%+v\n", expected, actual) } }
// Destroy collects the logs and tears down the cluster. func (f *Farmer) Destroy(t testing.TB) error { f.CollectLogs() if f.LogDir != "" { defer f.logf("logs copied to %s\n", f.AbsLogDir()) } wd, err := os.Getwd() if err != nil { wd = "acceptance" } baseDir := filepath.Join(wd, f.Cwd) if (t.Failed() && f.KeepCluster == KeepClusterFailed) || f.KeepCluster == KeepClusterAlways { t.Logf("not destroying; run:\n(cd %s && terraform destroy -force -state %s && rm %s)", baseDir, f.StateFile, f.StateFile) return nil } if err := f.Resize(0); err != nil { return err } return os.Remove(filepath.Join(baseDir, f.StateFile)) }