| | |
| | |
| | |
| |
|
| | package testing_test |
| |
|
| | import ( |
| | "flag" |
| | "fmt" |
| | "internal/testenv" |
| | "os" |
| | "os/exec" |
| | "regexp" |
| | "runtime" |
| | "strings" |
| | "testing" |
| | ) |
| |
|
| | var testPanicTest = flag.String("test_panic_test", "", "TestPanic: indicates which test should panic") |
| | var testPanicParallel = flag.Bool("test_panic_parallel", false, "TestPanic: run subtests in parallel") |
| | var testPanicCleanup = flag.Bool("test_panic_cleanup", false, "TestPanic: indicates whether test should call Cleanup") |
| | var testPanicCleanupPanic = flag.String("test_panic_cleanup_panic", "", "TestPanic: indicate whether test should call Cleanup function that panics") |
| |
|
| | func TestPanic(t *testing.T) { |
| | testenv.MustHaveExec(t) |
| |
|
| | testCases := []struct { |
| | desc string |
| | flags []string |
| | want string |
| | }{{ |
| | desc: "root test panics", |
| | flags: []string{"-test_panic_test=TestPanicHelper"}, |
| | want: ` |
| | --- FAIL: TestPanicHelper (N.NNs) |
| | panic_test.go:NNN: TestPanicHelper |
| | TestPanicHelper |
| | `, |
| | }, { |
| | desc: "subtest panics", |
| | flags: []string{"-test_panic_test=TestPanicHelper/1"}, |
| | want: ` |
| | --- FAIL: TestPanicHelper (N.NNs) |
| | panic_test.go:NNN: TestPanicHelper |
| | TestPanicHelper |
| | --- FAIL: TestPanicHelper/1 (N.NNs) |
| | panic_test.go:NNN: TestPanicHelper/1 |
| | TestPanicHelper/1 |
| | `, |
| | }, { |
| | desc: "subtest panics with cleanup", |
| | flags: []string{"-test_panic_test=TestPanicHelper/1", "-test_panic_cleanup"}, |
| | want: ` |
| | ran inner cleanup 1 |
| | ran middle cleanup 1 |
| | ran outer cleanup |
| | --- FAIL: TestPanicHelper (N.NNs) |
| | panic_test.go:NNN: TestPanicHelper |
| | TestPanicHelper |
| | --- FAIL: TestPanicHelper/1 (N.NNs) |
| | panic_test.go:NNN: TestPanicHelper/1 |
| | TestPanicHelper/1 |
| | `, |
| | }, { |
| | desc: "subtest panics with outer cleanup panic", |
| | flags: []string{"-test_panic_test=TestPanicHelper/1", "-test_panic_cleanup", "-test_panic_cleanup_panic=outer"}, |
| | want: ` |
| | ran inner cleanup 1 |
| | ran middle cleanup 1 |
| | ran outer cleanup |
| | --- FAIL: TestPanicHelper (N.NNs) |
| | panic_test.go:NNN: TestPanicHelper |
| | TestPanicHelper |
| | `, |
| | }, { |
| | desc: "subtest panics with middle cleanup panic", |
| | flags: []string{"-test_panic_test=TestPanicHelper/1", "-test_panic_cleanup", "-test_panic_cleanup_panic=middle"}, |
| | want: ` |
| | ran inner cleanup 1 |
| | ran middle cleanup 1 |
| | ran outer cleanup |
| | --- FAIL: TestPanicHelper (N.NNs) |
| | panic_test.go:NNN: TestPanicHelper |
| | TestPanicHelper |
| | --- FAIL: TestPanicHelper/1 (N.NNs) |
| | panic_test.go:NNN: TestPanicHelper/1 |
| | TestPanicHelper/1 |
| | `, |
| | }, { |
| | desc: "subtest panics with inner cleanup panic", |
| | flags: []string{"-test_panic_test=TestPanicHelper/1", "-test_panic_cleanup", "-test_panic_cleanup_panic=inner"}, |
| | want: ` |
| | ran inner cleanup 1 |
| | ran middle cleanup 1 |
| | ran outer cleanup |
| | --- FAIL: TestPanicHelper (N.NNs) |
| | panic_test.go:NNN: TestPanicHelper |
| | TestPanicHelper |
| | --- FAIL: TestPanicHelper/1 (N.NNs) |
| | panic_test.go:NNN: TestPanicHelper/1 |
| | TestPanicHelper/1 |
| | `, |
| | }, { |
| | desc: "parallel subtest panics with cleanup", |
| | flags: []string{"-test_panic_test=TestPanicHelper/1", "-test_panic_cleanup", "-test_panic_parallel"}, |
| | want: ` |
| | ran inner cleanup 1 |
| | ran middle cleanup 1 |
| | ran outer cleanup |
| | --- FAIL: TestPanicHelper (N.NNs) |
| | panic_test.go:NNN: TestPanicHelper |
| | TestPanicHelper |
| | --- FAIL: TestPanicHelper/1 (N.NNs) |
| | panic_test.go:NNN: TestPanicHelper/1 |
| | TestPanicHelper/1 |
| | `, |
| | }, { |
| | desc: "parallel subtest panics with outer cleanup panic", |
| | flags: []string{"-test_panic_test=TestPanicHelper/1", "-test_panic_cleanup", "-test_panic_cleanup_panic=outer", "-test_panic_parallel"}, |
| | want: ` |
| | ran inner cleanup 1 |
| | ran middle cleanup 1 |
| | ran outer cleanup |
| | --- FAIL: TestPanicHelper (N.NNs) |
| | panic_test.go:NNN: TestPanicHelper |
| | TestPanicHelper |
| | `, |
| | }, { |
| | desc: "parallel subtest panics with middle cleanup panic", |
| | flags: []string{"-test_panic_test=TestPanicHelper/1", "-test_panic_cleanup", "-test_panic_cleanup_panic=middle", "-test_panic_parallel"}, |
| | want: ` |
| | ran inner cleanup 1 |
| | ran middle cleanup 1 |
| | ran outer cleanup |
| | --- FAIL: TestPanicHelper (N.NNs) |
| | panic_test.go:NNN: TestPanicHelper |
| | TestPanicHelper |
| | --- FAIL: TestPanicHelper/1 (N.NNs) |
| | panic_test.go:NNN: TestPanicHelper/1 |
| | TestPanicHelper/1 |
| | `, |
| | }, { |
| | desc: "parallel subtest panics with inner cleanup panic", |
| | flags: []string{"-test_panic_test=TestPanicHelper/1", "-test_panic_cleanup", "-test_panic_cleanup_panic=inner", "-test_panic_parallel"}, |
| | want: ` |
| | ran inner cleanup 1 |
| | ran middle cleanup 1 |
| | ran outer cleanup |
| | --- FAIL: TestPanicHelper (N.NNs) |
| | panic_test.go:NNN: TestPanicHelper |
| | TestPanicHelper |
| | --- FAIL: TestPanicHelper/1 (N.NNs) |
| | panic_test.go:NNN: TestPanicHelper/1 |
| | TestPanicHelper/1 |
| | `, |
| | }} |
| | for _, tc := range testCases { |
| | t.Run(tc.desc, func(t *testing.T) { |
| | cmd := exec.Command(testenv.Executable(t), "-test.run=^TestPanicHelper$") |
| | cmd.Args = append(cmd.Args, tc.flags...) |
| | cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=1") |
| | b, _ := cmd.CombinedOutput() |
| | got := string(b) |
| | want := strings.TrimSpace(tc.want) |
| | re := makeRegexp(want) |
| | if ok, err := regexp.MatchString(re, got); !ok || err != nil { |
| | t.Errorf("output:\ngot:\n%s\nwant:\n%s", got, want) |
| | } |
| | }) |
| | } |
| | } |
| |
|
| | func makeRegexp(s string) string { |
| | s = regexp.QuoteMeta(s) |
| | s = strings.ReplaceAll(s, ":NNN:", `:\d+:`) |
| | s = strings.ReplaceAll(s, "N\\.NNs", `\d*\.\d*s`) |
| | return s |
| | } |
| |
|
| | func TestPanicHelper(t *testing.T) { |
| | if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" { |
| | return |
| | } |
| | t.Log(t.Name()) |
| | t.Output().Write([]byte(t.Name())) |
| | if t.Name() == *testPanicTest { |
| | panic("panic") |
| | } |
| | switch *testPanicCleanupPanic { |
| | case "", "outer", "middle", "inner": |
| | default: |
| | t.Fatalf("bad -test_panic_cleanup_panic: %s", *testPanicCleanupPanic) |
| | } |
| | t.Cleanup(func() { |
| | fmt.Println("ran outer cleanup") |
| | if *testPanicCleanupPanic == "outer" { |
| | panic("outer cleanup") |
| | } |
| | }) |
| | for i := 0; i < 3; i++ { |
| | t.Run(fmt.Sprintf("%v", i), func(t *testing.T) { |
| | chosen := t.Name() == *testPanicTest |
| | if chosen && *testPanicCleanup { |
| | t.Cleanup(func() { |
| | fmt.Printf("ran middle cleanup %d\n", i) |
| | if *testPanicCleanupPanic == "middle" { |
| | panic("middle cleanup") |
| | } |
| | }) |
| | } |
| | if chosen && *testPanicParallel { |
| | t.Parallel() |
| | } |
| | t.Log(t.Name()) |
| | t.Output().Write([]byte(t.Name())) |
| | if chosen { |
| | if *testPanicCleanup { |
| | t.Cleanup(func() { |
| | fmt.Printf("ran inner cleanup %d\n", i) |
| | if *testPanicCleanupPanic == "inner" { |
| | panic("inner cleanup") |
| | } |
| | }) |
| | } |
| | panic("panic") |
| | } |
| | }) |
| | } |
| | } |
| |
|
| | func TestMorePanic(t *testing.T) { |
| | testenv.MustHaveExec(t) |
| |
|
| | testCases := []struct { |
| | desc string |
| | flags []string |
| | want string |
| | }{ |
| | { |
| | desc: "Issue 48502: call runtime.Goexit in t.Cleanup after panic", |
| | flags: []string{"-test.run=^TestGoexitInCleanupAfterPanicHelper$"}, |
| | want: `panic: die |
| | panic: test executed panic(nil) or runtime.Goexit`, |
| | }, |
| | { |
| | desc: "Issue 48515: call t.Run in t.Cleanup should trigger panic", |
| | flags: []string{"-test.run=^TestCallRunInCleanupHelper$"}, |
| | want: `panic: testing: t.Run called during t.Cleanup`, |
| | }, |
| | } |
| |
|
| | for _, tc := range testCases { |
| | cmd := exec.Command(testenv.Executable(t), tc.flags...) |
| | cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=1") |
| | b, _ := cmd.CombinedOutput() |
| | got := string(b) |
| | want := tc.want |
| | re := makeRegexp(want) |
| | if ok, err := regexp.MatchString(re, got); !ok || err != nil { |
| | t.Errorf("output:\ngot:\n%s\nwant:\n%s", got, want) |
| | } |
| | } |
| | } |
| |
|
| | func TestCallRunInCleanupHelper(t *testing.T) { |
| | if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" { |
| | return |
| | } |
| |
|
| | t.Cleanup(func() { |
| | t.Run("in-cleanup", func(t *testing.T) { |
| | t.Log("must not be executed") |
| | }) |
| | }) |
| | } |
| |
|
| | func TestGoexitInCleanupAfterPanicHelper(t *testing.T) { |
| | if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" { |
| | return |
| | } |
| |
|
| | t.Cleanup(func() { runtime.Goexit() }) |
| | t.Parallel() |
| | panic("die") |
| | } |
| |
|