| // 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" |
| } |