blob: 4e46a46c55fc560f4d6b90285fe08b014c259fd5 [file] [log] [blame]
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package a
import (
"log"
"sync"
"testing"
)
func TestBadFatalf(t *testing.T) {
var wg sync.WaitGroup
defer wg.Wait()
for i := 0; i < 2; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
t.Fatalf("TestFailed: id = %v\n", id) // want "call to .+T.+Fatalf from a non-test goroutine"
}(i)
}
}
func TestOKErrorf(t *testing.T) {
var wg sync.WaitGroup
defer wg.Wait()
for i := 0; i < 2; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
t.Errorf("TestFailed: id = %v\n", id)
}(i)
}
}
func TestBadFatal(t *testing.T) {
var wg sync.WaitGroup
defer wg.Wait()
for i := 0; i < 2; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
t.Fatal("TestFailed") // want "call to .+T.+Fatal from a non-test goroutine"
}(i)
}
}
func f(t *testing.T, _ string) {
t.Fatal("TestFailed")
}
func g() {}
func TestBadFatalIssue47470(t *testing.T) {
go f(t, "failed test 1") // want "call to .+T.+Fatal from a non-test goroutine"
g := func(t *testing.T, _ string) {
t.Fatal("TestFailed")
}
go g(t, "failed test 2") // want "call to .+T.+Fatal from a non-test goroutine"
}
func BenchmarkBadFatalf(b *testing.B) {
var wg sync.WaitGroup
defer wg.Wait()
for i := 0; i < b.N; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
b.Fatalf("TestFailed: id = %v\n", id) // want "call to .+B.+Fatalf from a non-test goroutine"
}(i)
}
}
func BenchmarkBadFatal(b *testing.B) {
var wg sync.WaitGroup
defer wg.Wait()
for i := 0; i < b.N; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
b.Fatal("TestFailed") // want "call to .+B.+Fatal from a non-test goroutine"
}(i)
}
}
func BenchmarkOKErrorf(b *testing.B) {
var wg sync.WaitGroup
defer wg.Wait()
for i := 0; i < b.N; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
b.Errorf("TestFailed: %d", i)
}(i)
}
}
func BenchmarkBadFatalGoGo(b *testing.B) {
var wg sync.WaitGroup
defer wg.Wait()
for i := 0; i < b.N; i++ {
wg.Add(1)
go func(id int) {
go func() {
defer wg.Done()
b.Fatal("TestFailed") // want "call to .+B.+Fatal from a non-test goroutine"
}()
}(i)
}
if false {
defer b.Fatal("here")
}
if true {
go func() {
b.Fatal("in here") // want "call to .+B.+Fatal from a non-test goroutine"
}()
}
func() {
func() {
func() {
func() {
go func() {
b.Fatal("Here") // want "call to .+B.+Fatal from a non-test goroutine"
}()
}()
}()
}()
}()
_ = 10 * 10
_ = func() bool {
go b.Fatal("Failed") // want "call to .+B.+Fatal from a non-test goroutine"
return true
}
defer func() {
go b.Fatal("Here") // want "call to .+B.+Fatal from a non-test goroutine"
}()
}
func BenchmarkBadSkip(b *testing.B) {
for i := 0; i < b.N; i++ {
if i == 100 {
go b.Skip("Skipping") // want "call to .+B.+Skip from a non-test goroutine"
}
if i == 22 {
go func() {
go func() {
b.Skip("Skipping now") // want "call to .+B.+Skip from a non-test goroutine"
}()
}()
}
}
}
func TestBadSkip(t *testing.T) {
for i := 0; i < 1000; i++ {
if i == 100 {
go t.Skip("Skipping") // want "call to .+T.+Skip from a non-test goroutine"
}
if i == 22 {
go func() {
go func() {
t.Skip("Skipping now") // want "call to .+T.+Skip from a non-test goroutine"
}()
}()
}
}
}
func BenchmarkBadFailNow(b *testing.B) {
for i := 0; i < b.N; i++ {
if i == 100 {
go b.FailNow() // want "call to .+B.+FailNow from a non-test goroutine"
}
if i == 22 {
go func() {
go func() {
b.FailNow() // want "call to .+B.+FailNow from a non-test goroutine"
}()
}()
}
}
}
func TestBadFailNow(t *testing.T) {
for i := 0; i < 1000; i++ {
if i == 100 {
go t.FailNow() // want "call to .+T.+FailNow from a non-test goroutine"
}
if i == 22 {
go func() {
go func() {
t.FailNow() // want "call to .+T.+FailNow from a non-test goroutine"
}()
}()
}
}
}
func TestBadWithLoopCond(ty *testing.T) {
var wg sync.WaitGroup
defer wg.Wait()
for i := 0; i < 10; i++ {
wg.Add(1)
go func(id int) {
defer ty.Fatalf("Why") // want "call to .+T.+Fatalf from a non-test goroutine"
go func() {
for j := 0; j < 2; ty.FailNow() { // want "call to .+T.+FailNow from"
j++
ty.Errorf("Done here")
}
}()
}(i)
}
}
type customType int
func (ct *customType) Fatalf(fmtSpec string, args ...interface{}) {
if fmtSpec == "" {
panic("empty format specifier")
}
}
func (ct *customType) FailNow() {}
func (ct *customType) Skip() {}
func TestWithLogFatalf(t *testing.T) {
var wg sync.WaitGroup
defer wg.Wait()
for i := 0; i < 10; i++ {
wg.Add(1)
go func(id int) {
go func() {
for j := 0; j < 2; j++ {
log.Fatal("Done here")
}
}()
}(i)
}
}
func TestWithCustomType(t *testing.T) {
var wg sync.WaitGroup
defer wg.Wait()
ct := new(customType)
defer ct.FailNow()
defer ct.Skip()
for i := 0; i < 10; i++ {
wg.Add(1)
go func(id int) {
go func() {
for j := 0; j < 2; j++ {
ct.Fatalf("Done here: %d", i)
}
}()
}(i)
}
}
func helpTB(tb testing.TB) {
tb.FailNow()
}
func TestTB(t *testing.T) {
go helpTB(t) // want "call to .+TB.+FailNow from a non-test goroutine"
}
func TestIssue48124(t *testing.T) {
go helper(t) // want "call to .+T.+Skip from a non-test goroutine"
}
func TestEachCall(t *testing.T) {
go helper(t) // want "call to .+T.+Skip from a non-test goroutine"
go helper(t) // want "call to .+T.+Skip from a non-test goroutine"
}
func TestWithSubtest(t *testing.T) {
t.Run("name", func(t2 *testing.T) {
t.FailNow() // want "call to .+T.+FailNow on t defined outside of the subtest"
t2.Fatal()
})
f := func(t3 *testing.T) {
t.FailNow()
t3.Fatal()
}
t.Run("name", f) // want "call to .+T.+FailNow on t defined outside of the subtest"
g := func(t4 *testing.T) {
t.FailNow()
t4.Fatal()
}
g(t)
t.Run("name", helper)
go t.Run("name", func(t2 *testing.T) {
t.FailNow() // want "call to .+T.+FailNow on t defined outside of the subtest"
t2.Fatal()
})
}
func TestMultipleVariables(t *testing.T) {
{ // short decl
f, g := func(t1 *testing.T) {
t1.Fatal()
}, func(t2 *testing.T) {
t2.Error()
}
go f(t) // want "call to .+T.+Fatal from a non-test goroutine"
go g(t)
t.Run("name", f)
t.Run("name", g)
}
{ // var decl
var f, g = func(t1 *testing.T) {
t1.Fatal()
}, func(t2 *testing.T) {
t2.Error()
}
go f(t) // want "call to .+T.+Fatal from a non-test goroutine"
go g(t)
t.Run("name", f)
t.Run("name", g)
}
}
func BadIgnoresMultipleAssignments(t *testing.T) {
{
f := func(t1 *testing.T) {
t1.Fatal()
}
go f(t) // want "call to .+T.+Fatal from a non-test goroutine"
f = func(t2 *testing.T) {
t2.Error()
}
go f(t) // want "call to .+T.+Fatal from a non-test goroutine"
}
{
f := func(t1 *testing.T) {
t1.Error()
}
go f(t)
f = func(t2 *testing.T) {
t2.FailNow()
}
go f(t) // false negative
}
}
func TestGoDoesNotDescendIntoSubtest(t *testing.T) {
f := func(t2 *testing.T) {
g := func(t3 *testing.T) {
t3.Fatal() // fine
}
t2.Run("name", g)
t2.FailNow() // bad
}
go f(t) // want "call to .+T.+FailNow from a non-test goroutine"
}
func TestFreeVariableAssignedWithinEnclosing(t *testing.T) {
f := func(t2 *testing.T) {
inner := t
inner.FailNow()
}
go f(nil) // want "call to .+T.+FailNow from a non-test goroutine"
t.Run("name", func(t3 *testing.T) {
go f(nil) // want "call to .+T.+FailNow from a non-test goroutine"
})
// Without pointer analysis we cannot tell if inner is t or t2.
// So we accept a false negatives on the following examples.
t.Run("name", f)
go func(_ *testing.T) {
t.Run("name", f)
}(nil)
go t.Run("name", f)
}
func TestWithUnusedSelection(t *testing.T) {
go func() {
_ = t.FailNow
}()
t.Run("name", func(t2 *testing.T) {
_ = t.FailNow
})
}
func TestMethodExprsAreIgnored(t *testing.T) {
go func() {
(*testing.T).FailNow(t)
}()
}
func TestRecursive(t *testing.T) {
t.SkipNow()
go TestRecursive(t) // want "call to .+T.+SkipNow from a non-test goroutine"
t.Run("name", TestRecursive)
}
func TestMethodSelection(t *testing.T) {
var h helperType
go h.help(t) // want "call to .+T.+SkipNow from a non-test goroutine"
t.Run("name", h.help)
}
type helperType struct{}
func (h *helperType) help(t *testing.T) { t.SkipNow() }
func TestIssue63799a(t *testing.T) {
done := make(chan struct{})
go func() {
defer close(done)
t.Run("", func(t *testing.T) {
t.Fatal() // No warning. This is in a subtest.
})
}()
<-done
}
func TestIssue63799b(t *testing.T) {
// Simplified from go.dev/cl/538698
// nondet is some unspecified boolean placeholder.
var nondet func() bool
t.Run("nohup", func(t *testing.T) {
if nondet() {
t.Skip("ignored")
}
go t.Run("nohup-i", func(t *testing.T) {
t.Parallel()
if nondet() {
if nondet() {
t.Skip("go.dev/cl/538698 wanted to have skip here")
}
t.Error("ignored")
} else {
t.Log("ignored")
}
})
})
}
func TestIssue63849(t *testing.T) {
go func() {
helper(t) // False negative. We do not do an actual interprodecural reachability analysis.
}()
go helper(t) // want "call to .+T.+Skip from a non-test goroutine"
}