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