github.com/AndrienkoAleksandr/go@v0.0.19/src/testing/panic_test.go (about) 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 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 }