1// Copyright 2019 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 5package testing_test 6 7import ( 8 "flag" 9 "fmt" 10 "internal/testenv" 11 "os" 12 "os/exec" 13 "regexp" 14 "runtime" 15 "strings" 16 "testing" 17) 18 19var testPanicTest = flag.String("test_panic_test", "", "TestPanic: indicates which test should panic") 20var testPanicParallel = flag.Bool("test_panic_parallel", false, "TestPanic: run subtests in parallel") 21var testPanicCleanup = flag.Bool("test_panic_cleanup", false, "TestPanic: indicates whether test should call Cleanup") 22var testPanicCleanupPanic = flag.String("test_panic_cleanup_panic", "", "TestPanic: indicate whether test should call Cleanup function that panics") 23 24func 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: ` 51ran inner cleanup 1 52ran middle cleanup 1 53ran 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: ` 63ran inner cleanup 1 64ran middle cleanup 1 65ran 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: ` 73ran inner cleanup 1 74ran middle cleanup 1 75ran 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: ` 85ran inner cleanup 1 86ran middle cleanup 1 87ran 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: ` 97ran inner cleanup 1 98ran middle cleanup 1 99ran 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: ` 109ran inner cleanup 1 110ran middle cleanup 1 111ran 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: ` 119ran inner cleanup 1 120ran middle cleanup 1 121ran 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: ` 131ran inner cleanup 1 132ran middle cleanup 1 133ran 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 156func 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 163func 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 213func 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 247func 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 259func 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