Skip to content

Commit 53017a3

Browse files
committed
analysis/passes: report testing.Fatal* FailNow Skip* misuse in goroutines
Adds an analyzer to report an error if any tests or benchmarks have any *Fatal, FailNow, Skip* misuses in goroutines which are forbidden by the package testing, since those functions terminate the entire benchmark/test yet ideally one goroutine exiting shouldn't affect the entire benchmark/test. This first pass only works for plain goroutines and doesn't yet work with b.RunParallel. That'll be added in a subsequent CL after this one is reviewed and merged. Updates #5746 Change-Id: Ia47e5c9fd96ceced1ae9834b94f529f6ae2edaaa Reviewed-on: https://go-review.googlesource.com/c/tools/+/212920 Run-TryBot: Emmanuel Odeke <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Alan Donovan <[email protected]>
1 parent 7be0a67 commit 53017a3

File tree

3 files changed

+443
-0
lines changed

3 files changed

+443
-0
lines changed
Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
// Copyright 2020 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 a
6+
7+
import (
8+
"log"
9+
"sync"
10+
"testing"
11+
)
12+
13+
func TestBadFatalf(t *testing.T) {
14+
var wg sync.WaitGroup
15+
defer wg.Wait()
16+
17+
for i := 0; i < 2; i++ {
18+
wg.Add(1)
19+
go func(id int) {
20+
defer wg.Done()
21+
t.Fatalf("TestFailed: id = %v\n", id) // want "call to .+T.+Fatalf from a non-test goroutine"
22+
}(i)
23+
}
24+
}
25+
26+
func TestOKErrorf(t *testing.T) {
27+
var wg sync.WaitGroup
28+
defer wg.Wait()
29+
30+
for i := 0; i < 2; i++ {
31+
wg.Add(1)
32+
go func(id int) {
33+
defer wg.Done()
34+
t.Errorf("TestFailed: id = %v\n", id)
35+
}(i)
36+
}
37+
}
38+
39+
func BenchmarkBadFatalf(b *testing.B) {
40+
var wg sync.WaitGroup
41+
defer wg.Wait()
42+
43+
for i := 0; i < b.N; i++ {
44+
wg.Add(1)
45+
go func(id int) {
46+
defer wg.Done()
47+
b.Fatalf("TestFailed: id = %v\n", id) // want "call to .+B.+Fatalf from a non-test goroutine"
48+
}(i)
49+
}
50+
}
51+
52+
func TestBadFatal(t *testing.T) {
53+
var wg sync.WaitGroup
54+
defer wg.Wait()
55+
56+
for i := 0; i < 2; i++ {
57+
wg.Add(1)
58+
go func(id int) {
59+
defer wg.Done()
60+
t.Fatal("TestFailed") // want "call to .+T.+Fatal from a non-test goroutine"
61+
}(i)
62+
}
63+
}
64+
65+
func BenchmarkBadFatal(b *testing.B) {
66+
var wg sync.WaitGroup
67+
defer wg.Wait()
68+
69+
for i := 0; i < b.N; i++ {
70+
wg.Add(1)
71+
go func(id int) {
72+
defer wg.Done()
73+
b.Fatal("TestFailed") // want "call to .+B.+Fatal from a non-test goroutine"
74+
}(i)
75+
}
76+
}
77+
78+
func BenchmarkOKErrorf(b *testing.B) {
79+
var wg sync.WaitGroup
80+
defer wg.Wait()
81+
82+
for i := 0; i < b.N; i++ {
83+
wg.Add(1)
84+
go func(id int) {
85+
defer wg.Done()
86+
b.Errorf("TestFailed: %d", i)
87+
}(i)
88+
}
89+
}
90+
91+
func BenchmarkBadFatalGoGo(b *testing.B) {
92+
var wg sync.WaitGroup
93+
defer wg.Wait()
94+
95+
for i := 0; i < b.N; i++ {
96+
wg.Add(1)
97+
go func(id int) {
98+
go func() {
99+
defer wg.Done()
100+
b.Fatal("TestFailed") // want "call to .+B.+Fatal from a non-test goroutine"
101+
}()
102+
}(i)
103+
}
104+
105+
if false {
106+
defer b.Fatal("here")
107+
}
108+
109+
if true {
110+
go func() {
111+
b.Fatal("in here") // want "call to .+B.+Fatal from a non-test goroutine"
112+
}()
113+
}
114+
115+
func() {
116+
func() {
117+
func() {
118+
func() {
119+
go func() {
120+
b.Fatal("Here") // want "call to .+B.+Fatal from a non-test goroutine"
121+
}()
122+
}()
123+
}()
124+
}()
125+
}()
126+
127+
_ = 10 * 10
128+
_ = func() bool {
129+
go b.Fatal("Failed") // want "call to .+B.+Fatal from a non-test goroutine"
130+
return true
131+
}
132+
133+
defer func() {
134+
go b.Fatal("Here") // want "call to .+B.+Fatal from a non-test goroutine"
135+
}()
136+
}
137+
138+
func BenchmarkBadSkip(b *testing.B) {
139+
for i := 0; i < b.N; i++ {
140+
if i == 100 {
141+
go b.Skip("Skipping") // want "call to .+B.+Skip from a non-test goroutine"
142+
}
143+
if i == 22 {
144+
go func() {
145+
go func() {
146+
b.Skip("Skipping now") // want "call to .+B.+Skip from a non-test goroutine"
147+
}()
148+
}()
149+
}
150+
}
151+
}
152+
153+
func TestBadSkip(t *testing.T) {
154+
for i := 0; i < 1000; i++ {
155+
if i == 100 {
156+
go t.Skip("Skipping") // want "call to .+T.+Skip from a non-test goroutine"
157+
}
158+
if i == 22 {
159+
go func() {
160+
go func() {
161+
t.Skip("Skipping now") // want "call to .+T.+Skip from a non-test goroutine"
162+
}()
163+
}()
164+
}
165+
}
166+
}
167+
168+
func BenchmarkBadFailNow(b *testing.B) {
169+
for i := 0; i < b.N; i++ {
170+
if i == 100 {
171+
go b.FailNow() // want "call to .+B.+FailNow from a non-test goroutine"
172+
}
173+
if i == 22 {
174+
go func() {
175+
go func() {
176+
b.FailNow() // want "call to .+B.+FailNow from a non-test goroutine"
177+
}()
178+
}()
179+
}
180+
}
181+
}
182+
183+
func TestBadFailNow(t *testing.T) {
184+
for i := 0; i < 1000; i++ {
185+
if i == 100 {
186+
go t.FailNow() // want "call to .+T.+FailNow from a non-test goroutine"
187+
}
188+
if i == 22 {
189+
go func() {
190+
go func() {
191+
t.FailNow() // want "call to .+T.+FailNow from a non-test goroutine"
192+
}()
193+
}()
194+
}
195+
}
196+
}
197+
198+
func TestBadWithLoopCond(ty *testing.T) {
199+
var wg sync.WaitGroup
200+
defer wg.Wait()
201+
202+
for i := 0; i < 10; i++ {
203+
wg.Add(1)
204+
go func(id int) {
205+
defer ty.Fatalf("Why") // want "call to .+T.+Fatalf from a non-test goroutine"
206+
go func() {
207+
for j := 0; j < 2; ty.FailNow() { // want "call to .+T.+FailNow from"
208+
j++
209+
ty.Errorf("Done here")
210+
}
211+
}()
212+
}(i)
213+
}
214+
}
215+
216+
type customType int
217+
218+
func (ct *customType) Fatalf(fmtSpec string, args ...interface{}) {
219+
if fmtSpec == "" {
220+
panic("empty format specifier")
221+
}
222+
}
223+
224+
func (ct *customType) FailNow() {}
225+
func (ct *customType) Skip() {}
226+
227+
func TestWithLogFatalf(t *testing.T) {
228+
var wg sync.WaitGroup
229+
defer wg.Wait()
230+
231+
for i := 0; i < 10; i++ {
232+
wg.Add(1)
233+
go func(id int) {
234+
go func() {
235+
for j := 0; j < 2; j++ {
236+
log.Fatal("Done here")
237+
}
238+
}()
239+
}(i)
240+
}
241+
}
242+
243+
func TestWithCustomType(t *testing.T) {
244+
var wg sync.WaitGroup
245+
defer wg.Wait()
246+
247+
ct := new(customType)
248+
defer ct.FailNow()
249+
defer ct.Skip()
250+
251+
for i := 0; i < 10; i++ {
252+
wg.Add(1)
253+
go func(id int) {
254+
go func() {
255+
for j := 0; j < 2; j++ {
256+
ct.Fatalf("Done here: %d", i)
257+
}
258+
}()
259+
}(i)
260+
}
261+
}

0 commit comments

Comments
 (0)