Source file src/testing/panic_test.go

     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      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