Source file
src/testing/panic_test.go
1
2
3
4
5 package testing_test
6
7 import (
8 "flag"
9 "fmt"
10 "internal/testenv"
11 "os"
12 "os/exec"
13 "regexp"
14 "runtime"
15 "strings"
16 "testing"
17 )
18
19 var testPanicTest = flag.String("test_panic_test", "", "TestPanic: indicates which test should panic")
20 var testPanicParallel = flag.Bool("test_panic_parallel", false, "TestPanic: run subtests in parallel")
21 var testPanicCleanup = flag.Bool("test_panic_cleanup", false, "TestPanic: indicates whether test should call Cleanup")
22 var testPanicCleanupPanic = flag.String("test_panic_cleanup_panic", "", "TestPanic: indicate whether test should call Cleanup function that panics")
23
24 func TestPanic(t *testing.T) {
25 testenv.MustHaveExec(t)
26
27 testCases := []struct {
28 desc string
29 flags []string
30 want string
31 }{{
32 desc: "root test panics",
33 flags: []string{"-test_panic_test=TestPanicHelper"},
34 want: `
35 --- FAIL: TestPanicHelper (N.NNs)
36 panic_test.go:NNN: TestPanicHelper
37 TestPanicHelper
38 `,
39 }, {
40 desc: "subtest panics",
41 flags: []string{"-test_panic_test=TestPanicHelper/1"},
42 want: `
43 --- FAIL: TestPanicHelper (N.NNs)
44 panic_test.go:NNN: TestPanicHelper
45 TestPanicHelper
46 --- FAIL: TestPanicHelper/1 (N.NNs)
47 panic_test.go:NNN: TestPanicHelper/1
48 TestPanicHelper/1
49 `,
50 }, {
51 desc: "subtest panics with cleanup",
52 flags: []string{"-test_panic_test=TestPanicHelper/1", "-test_panic_cleanup"},
53 want: `
54 ran inner cleanup 1
55 ran middle cleanup 1
56 ran outer cleanup
57 --- FAIL: TestPanicHelper (N.NNs)
58 panic_test.go:NNN: TestPanicHelper
59 TestPanicHelper
60 --- FAIL: TestPanicHelper/1 (N.NNs)
61 panic_test.go:NNN: TestPanicHelper/1
62 TestPanicHelper/1
63 `,
64 }, {
65 desc: "subtest panics with outer cleanup panic",
66 flags: []string{"-test_panic_test=TestPanicHelper/1", "-test_panic_cleanup", "-test_panic_cleanup_panic=outer"},
67 want: `
68 ran inner cleanup 1
69 ran middle cleanup 1
70 ran outer cleanup
71 --- FAIL: TestPanicHelper (N.NNs)
72 panic_test.go:NNN: TestPanicHelper
73 TestPanicHelper
74 `,
75 }, {
76 desc: "subtest panics with middle cleanup panic",
77 flags: []string{"-test_panic_test=TestPanicHelper/1", "-test_panic_cleanup", "-test_panic_cleanup_panic=middle"},
78 want: `
79 ran inner cleanup 1
80 ran middle cleanup 1
81 ran outer cleanup
82 --- FAIL: TestPanicHelper (N.NNs)
83 panic_test.go:NNN: TestPanicHelper
84 TestPanicHelper
85 --- FAIL: TestPanicHelper/1 (N.NNs)
86 panic_test.go:NNN: TestPanicHelper/1
87 TestPanicHelper/1
88 `,
89 }, {
90 desc: "subtest panics with inner cleanup panic",
91 flags: []string{"-test_panic_test=TestPanicHelper/1", "-test_panic_cleanup", "-test_panic_cleanup_panic=inner"},
92 want: `
93 ran inner cleanup 1
94 ran middle cleanup 1
95 ran outer cleanup
96 --- FAIL: TestPanicHelper (N.NNs)
97 panic_test.go:NNN: TestPanicHelper
98 TestPanicHelper
99 --- FAIL: TestPanicHelper/1 (N.NNs)
100 panic_test.go:NNN: TestPanicHelper/1
101 TestPanicHelper/1
102 `,
103 }, {
104 desc: "parallel subtest panics with cleanup",
105 flags: []string{"-test_panic_test=TestPanicHelper/1", "-test_panic_cleanup", "-test_panic_parallel"},
106 want: `
107 ran inner cleanup 1
108 ran middle cleanup 1
109 ran outer cleanup
110 --- FAIL: TestPanicHelper (N.NNs)
111 panic_test.go:NNN: TestPanicHelper
112 TestPanicHelper
113 --- FAIL: TestPanicHelper/1 (N.NNs)
114 panic_test.go:NNN: TestPanicHelper/1
115 TestPanicHelper/1
116 `,
117 }, {
118 desc: "parallel subtest panics with outer cleanup panic",
119 flags: []string{"-test_panic_test=TestPanicHelper/1", "-test_panic_cleanup", "-test_panic_cleanup_panic=outer", "-test_panic_parallel"},
120 want: `
121 ran inner cleanup 1
122 ran middle cleanup 1
123 ran outer cleanup
124 --- FAIL: TestPanicHelper (N.NNs)
125 panic_test.go:NNN: TestPanicHelper
126 TestPanicHelper
127 `,
128 }, {
129 desc: "parallel subtest panics with middle cleanup panic",
130 flags: []string{"-test_panic_test=TestPanicHelper/1", "-test_panic_cleanup", "-test_panic_cleanup_panic=middle", "-test_panic_parallel"},
131 want: `
132 ran inner cleanup 1
133 ran middle cleanup 1
134 ran outer cleanup
135 --- FAIL: TestPanicHelper (N.NNs)
136 panic_test.go:NNN: TestPanicHelper
137 TestPanicHelper
138 --- FAIL: TestPanicHelper/1 (N.NNs)
139 panic_test.go:NNN: TestPanicHelper/1
140 TestPanicHelper/1
141 `,
142 }, {
143 desc: "parallel subtest panics with inner cleanup panic",
144 flags: []string{"-test_panic_test=TestPanicHelper/1", "-test_panic_cleanup", "-test_panic_cleanup_panic=inner", "-test_panic_parallel"},
145 want: `
146 ran inner cleanup 1
147 ran middle cleanup 1
148 ran outer cleanup
149 --- FAIL: TestPanicHelper (N.NNs)
150 panic_test.go:NNN: TestPanicHelper
151 TestPanicHelper
152 --- FAIL: TestPanicHelper/1 (N.NNs)
153 panic_test.go:NNN: TestPanicHelper/1
154 TestPanicHelper/1
155 `,
156 }}
157 for _, tc := range testCases {
158 t.Run(tc.desc, func(t *testing.T) {
159 cmd := exec.Command(testenv.Executable(t), "-test.run=^TestPanicHelper$")
160 cmd.Args = append(cmd.Args, tc.flags...)
161 cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=1")
162 b, _ := cmd.CombinedOutput()
163 got := string(b)
164 want := strings.TrimSpace(tc.want)
165 re := makeRegexp(want)
166 if ok, err := regexp.MatchString(re, got); !ok || err != nil {
167 t.Errorf("output:\ngot:\n%s\nwant:\n%s", got, want)
168 }
169 })
170 }
171 }
172
173 func makeRegexp(s string) string {
174 s = regexp.QuoteMeta(s)
175 s = strings.ReplaceAll(s, ":NNN:", `:\d+:`)
176 s = strings.ReplaceAll(s, "N\\.NNs", `\d*\.\d*s`)
177 return s
178 }
179
180 func TestPanicHelper(t *testing.T) {
181 if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
182 return
183 }
184 t.Log(t.Name())
185 t.Output().Write([]byte(t.Name()))
186 if t.Name() == *testPanicTest {
187 panic("panic")
188 }
189 switch *testPanicCleanupPanic {
190 case "", "outer", "middle", "inner":
191 default:
192 t.Fatalf("bad -test_panic_cleanup_panic: %s", *testPanicCleanupPanic)
193 }
194 t.Cleanup(func() {
195 fmt.Println("ran outer cleanup")
196 if *testPanicCleanupPanic == "outer" {
197 panic("outer cleanup")
198 }
199 })
200 for i := 0; i < 3; i++ {
201 i := i
202 t.Run(fmt.Sprintf("%v", i), func(t *testing.T) {
203 chosen := t.Name() == *testPanicTest
204 if chosen && *testPanicCleanup {
205 t.Cleanup(func() {
206 fmt.Printf("ran middle cleanup %d\n", i)
207 if *testPanicCleanupPanic == "middle" {
208 panic("middle cleanup")
209 }
210 })
211 }
212 if chosen && *testPanicParallel {
213 t.Parallel()
214 }
215 t.Log(t.Name())
216 t.Output().Write([]byte(t.Name()))
217 if chosen {
218 if *testPanicCleanup {
219 t.Cleanup(func() {
220 fmt.Printf("ran inner cleanup %d\n", i)
221 if *testPanicCleanupPanic == "inner" {
222 panic("inner cleanup")
223 }
224 })
225 }
226 panic("panic")
227 }
228 })
229 }
230 }
231
232 func TestMorePanic(t *testing.T) {
233 testenv.MustHaveExec(t)
234
235 testCases := []struct {
236 desc string
237 flags []string
238 want string
239 }{
240 {
241 desc: "Issue 48502: call runtime.Goexit in t.Cleanup after panic",
242 flags: []string{"-test.run=^TestGoexitInCleanupAfterPanicHelper$"},
243 want: `panic: die
244 panic: test executed panic(nil) or runtime.Goexit`,
245 },
246 {
247 desc: "Issue 48515: call t.Run in t.Cleanup should trigger panic",
248 flags: []string{"-test.run=^TestCallRunInCleanupHelper$"},
249 want: `panic: testing: t.Run called during t.Cleanup`,
250 },
251 }
252
253 for _, tc := range testCases {
254 cmd := exec.Command(testenv.Executable(t), tc.flags...)
255 cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=1")
256 b, _ := cmd.CombinedOutput()
257 got := string(b)
258 want := tc.want
259 re := makeRegexp(want)
260 if ok, err := regexp.MatchString(re, got); !ok || err != nil {
261 t.Errorf("output:\ngot:\n%s\nwant:\n%s", got, want)
262 }
263 }
264 }
265
266 func TestCallRunInCleanupHelper(t *testing.T) {
267 if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
268 return
269 }
270
271 t.Cleanup(func() {
272 t.Run("in-cleanup", func(t *testing.T) {
273 t.Log("must not be executed")
274 })
275 })
276 }
277
278 func TestGoexitInCleanupAfterPanicHelper(t *testing.T) {
279 if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
280 return
281 }
282
283 t.Cleanup(func() { runtime.Goexit() })
284 t.Parallel()
285 panic("die")
286 }
287
View as plain text