Source file src/runtime/crash_test.go

     1  // Copyright 2012 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 runtime_test
     6  
     7  import (
     8  	"bytes"
     9  	"context"
    10  	"errors"
    11  	"flag"
    12  	"fmt"
    13  	"internal/asan"
    14  	"internal/msan"
    15  	"internal/profile"
    16  	"internal/race"
    17  	"internal/testenv"
    18  	traceparse "internal/trace"
    19  	"io"
    20  	"log"
    21  	"os"
    22  	"os/exec"
    23  	"path/filepath"
    24  	"regexp"
    25  	"runtime"
    26  	"runtime/trace"
    27  	"strings"
    28  	"sync"
    29  	"testing"
    30  	"time"
    31  )
    32  
    33  var toRemove []string
    34  
    35  const entrypointVar = "RUNTIME_TEST_ENTRYPOINT"
    36  
    37  func TestMain(m *testing.M) {
    38  	switch entrypoint := os.Getenv(entrypointVar); entrypoint {
    39  	case "panic":
    40  		crashViaPanic()
    41  		panic("unreachable")
    42  	case "trap":
    43  		crashViaTrap()
    44  		panic("unreachable")
    45  	default:
    46  		log.Fatalf("invalid %s: %q", entrypointVar, entrypoint)
    47  	case "":
    48  		// fall through to normal behavior
    49  	}
    50  
    51  	_, coreErrBefore := os.Stat("core")
    52  
    53  	status := m.Run()
    54  	for _, file := range toRemove {
    55  		os.RemoveAll(file)
    56  	}
    57  
    58  	_, coreErrAfter := os.Stat("core")
    59  	if coreErrBefore != nil && coreErrAfter == nil {
    60  		fmt.Fprintln(os.Stderr, "runtime.test: some test left a core file behind")
    61  		if status == 0 {
    62  			status = 1
    63  		}
    64  	}
    65  
    66  	os.Exit(status)
    67  }
    68  
    69  var testprog struct {
    70  	sync.Mutex
    71  	dir    string
    72  	target map[string]*buildexe
    73  }
    74  
    75  type buildexe struct {
    76  	once sync.Once
    77  	exe  string
    78  	err  error
    79  }
    80  
    81  func runTestProg(t *testing.T, binary, name string, env ...string) string {
    82  	if *flagQuick {
    83  		t.Skip("-quick")
    84  	}
    85  
    86  	testenv.MustHaveGoBuild(t)
    87  	t.Helper()
    88  
    89  	exe, err := buildTestProg(t, binary)
    90  	if err != nil {
    91  		t.Fatal(err)
    92  	}
    93  
    94  	return runBuiltTestProg(t, exe, name, env...)
    95  }
    96  
    97  func runBuiltTestProg(t *testing.T, exe, name string, env ...string) string {
    98  	t.Helper()
    99  
   100  	if *flagQuick {
   101  		t.Skip("-quick")
   102  	}
   103  
   104  	start := time.Now()
   105  
   106  	cmd := testenv.CleanCmdEnv(testenv.Command(t, exe, name))
   107  	cmd.Env = append(cmd.Env, env...)
   108  	if testing.Short() {
   109  		cmd.Env = append(cmd.Env, "RUNTIME_TEST_SHORT=1")
   110  	}
   111  	out, err := cmd.CombinedOutput()
   112  	if err == nil {
   113  		t.Logf("%v (%v): ok", cmd, time.Since(start))
   114  	} else {
   115  		if _, ok := err.(*exec.ExitError); ok {
   116  			t.Logf("%v: %v", cmd, err)
   117  		} else if errors.Is(err, exec.ErrWaitDelay) {
   118  			t.Fatalf("%v: %v", cmd, err)
   119  		} else {
   120  			t.Fatalf("%v failed to start: %v", cmd, err)
   121  		}
   122  	}
   123  	return string(out)
   124  }
   125  
   126  var serializeBuild = make(chan bool, 2)
   127  
   128  func buildTestProg(t *testing.T, binary string, flags ...string) (string, error) {
   129  	if *flagQuick {
   130  		t.Skip("-quick")
   131  	}
   132  	testenv.MustHaveGoBuild(t)
   133  
   134  	testprog.Lock()
   135  	if testprog.dir == "" {
   136  		dir, err := os.MkdirTemp("", "go-build")
   137  		if err != nil {
   138  			t.Fatalf("failed to create temp directory: %v", err)
   139  		}
   140  		testprog.dir = dir
   141  		toRemove = append(toRemove, dir)
   142  	}
   143  
   144  	if testprog.target == nil {
   145  		testprog.target = make(map[string]*buildexe)
   146  	}
   147  	name := binary
   148  	if len(flags) > 0 {
   149  		name += "_" + strings.Join(flags, "_")
   150  	}
   151  	target, ok := testprog.target[name]
   152  	if !ok {
   153  		target = &buildexe{}
   154  		testprog.target[name] = target
   155  	}
   156  
   157  	dir := testprog.dir
   158  
   159  	// Unlock testprog while actually building, so that other
   160  	// tests can look up executables that were already built.
   161  	testprog.Unlock()
   162  
   163  	target.once.Do(func() {
   164  		// Only do two "go build"'s at a time,
   165  		// to keep load from getting too high.
   166  		serializeBuild <- true
   167  		defer func() { <-serializeBuild }()
   168  
   169  		// Don't get confused if testenv.GoToolPath calls t.Skip.
   170  		target.err = errors.New("building test called t.Skip")
   171  
   172  		if asan.Enabled {
   173  			flags = append(flags, "-asan")
   174  		}
   175  		if msan.Enabled {
   176  			flags = append(flags, "-msan")
   177  		}
   178  		if race.Enabled {
   179  			flags = append(flags, "-race")
   180  		}
   181  
   182  		exe := filepath.Join(dir, name+".exe")
   183  
   184  		start := time.Now()
   185  		cmd := exec.Command(testenv.GoToolPath(t), append([]string{"build", "-o", exe}, flags...)...)
   186  		t.Logf("running %v", cmd)
   187  		cmd.Dir = "testdata/" + binary
   188  		cmd = testenv.CleanCmdEnv(cmd)
   189  
   190  		// Add the rangefunc GOEXPERIMENT unconditionally since some tests depend on it.
   191  		// TODO(61405): Remove this once it's enabled by default.
   192  		edited := false
   193  		for i := range cmd.Env {
   194  			e := cmd.Env[i]
   195  			if _, vars, ok := strings.Cut(e, "GOEXPERIMENT="); ok {
   196  				cmd.Env[i] = "GOEXPERIMENT=" + vars + ",rangefunc"
   197  				edited = true
   198  			}
   199  		}
   200  		if !edited {
   201  			cmd.Env = append(cmd.Env, "GOEXPERIMENT=rangefunc")
   202  		}
   203  
   204  		out, err := cmd.CombinedOutput()
   205  		if err != nil {
   206  			target.err = fmt.Errorf("building %s %v: %v\n%s", binary, flags, err, out)
   207  		} else {
   208  			t.Logf("built %v in %v", name, time.Since(start))
   209  			target.exe = exe
   210  			target.err = nil
   211  		}
   212  	})
   213  
   214  	return target.exe, target.err
   215  }
   216  
   217  func TestVDSO(t *testing.T) {
   218  	t.Parallel()
   219  	output := runTestProg(t, "testprog", "SignalInVDSO")
   220  	want := "success\n"
   221  	if output != want {
   222  		t.Fatalf("output:\n%s\n\nwanted:\n%s", output, want)
   223  	}
   224  }
   225  
   226  func testCrashHandler(t *testing.T, cgo bool) {
   227  	type crashTest struct {
   228  		Cgo bool
   229  	}
   230  	var output string
   231  	if cgo {
   232  		if runtime.GOOS == "freebsd" && race.Enabled {
   233  			t.Skipf("race + cgo freebsd not supported. See https://go.dev/issue/73788.")
   234  		}
   235  		output = runTestProg(t, "testprogcgo", "Crash")
   236  	} else {
   237  		output = runTestProg(t, "testprog", "Crash")
   238  	}
   239  	want := "main: recovered done\nnew-thread: recovered done\nsecond-new-thread: recovered done\nmain-again: recovered done\n"
   240  	if output != want {
   241  		t.Fatalf("output:\n%s\n\nwanted:\n%s", output, want)
   242  	}
   243  }
   244  
   245  func TestCrashHandler(t *testing.T) {
   246  	testCrashHandler(t, false)
   247  }
   248  
   249  var deadlockBuildTypes = testenv.SpecialBuildTypes{
   250  	// External linking brings in cgo, causing deadlock detection not working.
   251  	Cgo:  false,
   252  	Asan: asan.Enabled,
   253  	Msan: msan.Enabled,
   254  	Race: race.Enabled,
   255  }
   256  
   257  func testDeadlock(t *testing.T, name string) {
   258  	// External linking brings in cgo, causing deadlock detection not working.
   259  	testenv.MustInternalLink(t, deadlockBuildTypes)
   260  
   261  	output := runTestProg(t, "testprog", name)
   262  	want := "fatal error: all goroutines are asleep - deadlock!\n"
   263  	if !strings.HasPrefix(output, want) {
   264  		t.Fatalf("output does not start with %q:\n%s", want, output)
   265  	}
   266  }
   267  
   268  func TestSimpleDeadlock(t *testing.T) {
   269  	testDeadlock(t, "SimpleDeadlock")
   270  }
   271  
   272  func TestInitDeadlock(t *testing.T) {
   273  	testDeadlock(t, "InitDeadlock")
   274  }
   275  
   276  func TestLockedDeadlock(t *testing.T) {
   277  	testDeadlock(t, "LockedDeadlock")
   278  }
   279  
   280  func TestLockedDeadlock2(t *testing.T) {
   281  	testDeadlock(t, "LockedDeadlock2")
   282  }
   283  
   284  func TestGoexitDeadlock(t *testing.T) {
   285  	// External linking brings in cgo, causing deadlock detection not working.
   286  	testenv.MustInternalLink(t, deadlockBuildTypes)
   287  
   288  	output := runTestProg(t, "testprog", "GoexitDeadlock")
   289  	want := "no goroutines (main called runtime.Goexit) - deadlock!"
   290  	if !strings.Contains(output, want) {
   291  		t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
   292  	}
   293  }
   294  
   295  func TestStackOverflow(t *testing.T) {
   296  	output := runTestProg(t, "testprog", "StackOverflow")
   297  	want := []string{
   298  		"runtime: goroutine stack exceeds 1474560-byte limit\n",
   299  		"fatal error: stack overflow",
   300  		// information about the current SP and stack bounds
   301  		"runtime: sp=",
   302  		"stack=[",
   303  	}
   304  	if !strings.HasPrefix(output, want[0]) {
   305  		t.Errorf("output does not start with %q", want[0])
   306  	}
   307  	for _, s := range want[1:] {
   308  		if !strings.Contains(output, s) {
   309  			t.Errorf("output does not contain %q", s)
   310  		}
   311  	}
   312  	if t.Failed() {
   313  		t.Logf("output:\n%s", output)
   314  	}
   315  }
   316  
   317  func TestThreadExhaustion(t *testing.T) {
   318  	output := runTestProg(t, "testprog", "ThreadExhaustion")
   319  	want := "runtime: program exceeds 10-thread limit\nfatal error: thread exhaustion"
   320  	if !strings.HasPrefix(output, want) {
   321  		t.Fatalf("output does not start with %q:\n%s", want, output)
   322  	}
   323  }
   324  
   325  func TestRecursivePanic(t *testing.T) {
   326  	output := runTestProg(t, "testprog", "RecursivePanic")
   327  	want := `wrap: bad
   328  panic: again
   329  
   330  `
   331  	if !strings.HasPrefix(output, want) {
   332  		t.Fatalf("output does not start with %q:\n%s", want, output)
   333  	}
   334  
   335  }
   336  
   337  func TestRecursivePanic2(t *testing.T) {
   338  	output := runTestProg(t, "testprog", "RecursivePanic2")
   339  	want := `first panic
   340  second panic
   341  panic: third panic
   342  
   343  `
   344  	if !strings.HasPrefix(output, want) {
   345  		t.Fatalf("output does not start with %q:\n%s", want, output)
   346  	}
   347  
   348  }
   349  
   350  func TestRecursivePanic3(t *testing.T) {
   351  	output := runTestProg(t, "testprog", "RecursivePanic3")
   352  	want := `panic: first panic
   353  
   354  `
   355  	if !strings.HasPrefix(output, want) {
   356  		t.Fatalf("output does not start with %q:\n%s", want, output)
   357  	}
   358  
   359  }
   360  
   361  func TestRecursivePanic4(t *testing.T) {
   362  	output := runTestProg(t, "testprog", "RecursivePanic4")
   363  	want := `panic: first panic [recovered]
   364  	panic: second panic
   365  `
   366  	if !strings.HasPrefix(output, want) {
   367  		t.Fatalf("output does not start with %q:\n%s", want, output)
   368  	}
   369  
   370  }
   371  
   372  func TestRecursivePanic5(t *testing.T) {
   373  	output := runTestProg(t, "testprog", "RecursivePanic5")
   374  	want := `first panic
   375  second panic
   376  panic: third panic
   377  `
   378  	if !strings.HasPrefix(output, want) {
   379  		t.Fatalf("output does not start with %q:\n%s", want, output)
   380  	}
   381  
   382  }
   383  
   384  func TestRepanickedPanic(t *testing.T) {
   385  	output := runTestProg(t, "testprog", "RepanickedPanic")
   386  	want := `panic: message [recovered, repanicked]
   387  `
   388  	if !strings.HasPrefix(output, want) {
   389  		t.Fatalf("output does not start with %q:\n%s", want, output)
   390  	}
   391  }
   392  
   393  func TestRepanickedMiddlePanic(t *testing.T) {
   394  	output := runTestProg(t, "testprog", "RepanickedMiddlePanic")
   395  	want := `panic: inner [recovered]
   396  	panic: middle [recovered, repanicked]
   397  	panic: outer
   398  `
   399  	if !strings.HasPrefix(output, want) {
   400  		t.Fatalf("output does not start with %q:\n%s", want, output)
   401  	}
   402  }
   403  
   404  func TestRepanickedPanicSandwich(t *testing.T) {
   405  	output := runTestProg(t, "testprog", "RepanickedPanicSandwich")
   406  	want := `panic: outer [recovered]
   407  	panic: inner [recovered]
   408  	panic: outer
   409  `
   410  	if !strings.HasPrefix(output, want) {
   411  		t.Fatalf("output does not start with %q:\n%s", want, output)
   412  	}
   413  }
   414  
   415  func TestGoexitCrash(t *testing.T) {
   416  	// External linking brings in cgo, causing deadlock detection not working.
   417  	testenv.MustInternalLink(t, deadlockBuildTypes)
   418  
   419  	output := runTestProg(t, "testprog", "GoexitExit")
   420  	want := "no goroutines (main called runtime.Goexit) - deadlock!"
   421  	if !strings.Contains(output, want) {
   422  		t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
   423  	}
   424  }
   425  
   426  func TestGoexitDefer(t *testing.T) {
   427  	c := make(chan struct{})
   428  	go func() {
   429  		defer func() {
   430  			r := recover()
   431  			if r != nil {
   432  				t.Errorf("non-nil recover during Goexit")
   433  			}
   434  			c <- struct{}{}
   435  		}()
   436  		runtime.Goexit()
   437  	}()
   438  	// Note: if the defer fails to run, we will get a deadlock here
   439  	<-c
   440  }
   441  
   442  func TestGoNil(t *testing.T) {
   443  	output := runTestProg(t, "testprog", "GoNil")
   444  	want := "go of nil func value"
   445  	if !strings.Contains(output, want) {
   446  		t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
   447  	}
   448  }
   449  
   450  func TestMainGoroutineID(t *testing.T) {
   451  	output := runTestProg(t, "testprog", "MainGoroutineID")
   452  	want := "panic: test\n\ngoroutine 1 [running]:\n"
   453  	if !strings.HasPrefix(output, want) {
   454  		t.Fatalf("output does not start with %q:\n%s", want, output)
   455  	}
   456  }
   457  
   458  func TestNoHelperGoroutines(t *testing.T) {
   459  	output := runTestProg(t, "testprog", "NoHelperGoroutines")
   460  	matches := regexp.MustCompile(`goroutine [0-9]+ \[`).FindAllStringSubmatch(output, -1)
   461  	if len(matches) != 1 || matches[0][0] != "goroutine 1 [" {
   462  		t.Fatalf("want to see only goroutine 1, see:\n%s", output)
   463  	}
   464  }
   465  
   466  func TestBreakpoint(t *testing.T) {
   467  	output := runTestProg(t, "testprog", "Breakpoint")
   468  	// If runtime.Breakpoint() is inlined, then the stack trace prints
   469  	// "runtime.Breakpoint(...)" instead of "runtime.Breakpoint()".
   470  	want := "runtime.Breakpoint("
   471  	if !strings.Contains(output, want) {
   472  		t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
   473  	}
   474  }
   475  
   476  func TestGoexitInPanic(t *testing.T) {
   477  	// External linking brings in cgo, causing deadlock detection not working.
   478  	testenv.MustInternalLink(t, deadlockBuildTypes)
   479  
   480  	// see issue 8774: this code used to trigger an infinite recursion
   481  	output := runTestProg(t, "testprog", "GoexitInPanic")
   482  	want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!"
   483  	if !strings.HasPrefix(output, want) {
   484  		t.Fatalf("output does not start with %q:\n%s", want, output)
   485  	}
   486  }
   487  
   488  // Issue 14965: Runtime panics should be of type runtime.Error
   489  func TestRuntimePanicWithRuntimeError(t *testing.T) {
   490  	testCases := [...]func(){
   491  		0: func() {
   492  			var m map[uint64]bool
   493  			m[1234] = true
   494  		},
   495  		1: func() {
   496  			ch := make(chan struct{})
   497  			close(ch)
   498  			close(ch)
   499  		},
   500  		2: func() {
   501  			var ch = make(chan struct{})
   502  			close(ch)
   503  			ch <- struct{}{}
   504  		},
   505  		3: func() {
   506  			var s = make([]int, 2)
   507  			_ = s[2]
   508  		},
   509  		4: func() {
   510  			n := -1
   511  			_ = make(chan bool, n)
   512  		},
   513  		5: func() {
   514  			close((chan bool)(nil))
   515  		},
   516  	}
   517  
   518  	for i, fn := range testCases {
   519  		got := panicValue(fn)
   520  		if _, ok := got.(runtime.Error); !ok {
   521  			t.Errorf("test #%d: recovered value %v(type %T) does not implement runtime.Error", i, got, got)
   522  		}
   523  	}
   524  }
   525  
   526  func panicValue(fn func()) (recovered any) {
   527  	defer func() {
   528  		recovered = recover()
   529  	}()
   530  	fn()
   531  	return
   532  }
   533  
   534  func TestPanicAfterGoexit(t *testing.T) {
   535  	// an uncaught panic should still work after goexit
   536  	output := runTestProg(t, "testprog", "PanicAfterGoexit")
   537  	want := "panic: hello"
   538  	if !strings.HasPrefix(output, want) {
   539  		t.Fatalf("output does not start with %q:\n%s", want, output)
   540  	}
   541  }
   542  
   543  func TestRecoveredPanicAfterGoexit(t *testing.T) {
   544  	// External linking brings in cgo, causing deadlock detection not working.
   545  	testenv.MustInternalLink(t, deadlockBuildTypes)
   546  
   547  	output := runTestProg(t, "testprog", "RecoveredPanicAfterGoexit")
   548  	want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!"
   549  	if !strings.HasPrefix(output, want) {
   550  		t.Fatalf("output does not start with %q:\n%s", want, output)
   551  	}
   552  }
   553  
   554  func TestRecoverBeforePanicAfterGoexit(t *testing.T) {
   555  	// External linking brings in cgo, causing deadlock detection not working.
   556  	testenv.MustInternalLink(t, deadlockBuildTypes)
   557  
   558  	t.Parallel()
   559  	output := runTestProg(t, "testprog", "RecoverBeforePanicAfterGoexit")
   560  	want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!"
   561  	if !strings.HasPrefix(output, want) {
   562  		t.Fatalf("output does not start with %q:\n%s", want, output)
   563  	}
   564  }
   565  
   566  func TestRecoverBeforePanicAfterGoexit2(t *testing.T) {
   567  	// External linking brings in cgo, causing deadlock detection not working.
   568  	testenv.MustInternalLink(t, deadlockBuildTypes)
   569  
   570  	t.Parallel()
   571  	output := runTestProg(t, "testprog", "RecoverBeforePanicAfterGoexit2")
   572  	want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!"
   573  	if !strings.HasPrefix(output, want) {
   574  		t.Fatalf("output does not start with %q:\n%s", want, output)
   575  	}
   576  }
   577  
   578  func TestNetpollDeadlock(t *testing.T) {
   579  	t.Parallel()
   580  	output := runTestProg(t, "testprognet", "NetpollDeadlock")
   581  	want := "done\n"
   582  	if !strings.HasSuffix(output, want) {
   583  		t.Fatalf("output does not start with %q:\n%s", want, output)
   584  	}
   585  }
   586  
   587  func TestPanicTraceback(t *testing.T) {
   588  	t.Parallel()
   589  	output := runTestProg(t, "testprog", "PanicTraceback")
   590  	want := "panic: hello\n\tpanic: panic pt2\n\tpanic: panic pt1\n"
   591  	if !strings.HasPrefix(output, want) {
   592  		t.Fatalf("output does not start with %q:\n%s", want, output)
   593  	}
   594  
   595  	// Check functions in the traceback.
   596  	fns := []string{"main.pt1.func1", "panic", "main.pt2.func1", "panic", "main.pt2", "main.pt1"}
   597  	for _, fn := range fns {
   598  		re := regexp.MustCompile(`(?m)^` + regexp.QuoteMeta(fn) + `\(.*\n`)
   599  		idx := re.FindStringIndex(output)
   600  		if idx == nil {
   601  			t.Fatalf("expected %q function in traceback:\n%s", fn, output)
   602  		}
   603  		output = output[idx[1]:]
   604  	}
   605  }
   606  
   607  func testPanicDeadlock(t *testing.T, name string, want string) {
   608  	// test issue 14432
   609  	output := runTestProg(t, "testprog", name)
   610  	if !strings.HasPrefix(output, want) {
   611  		t.Fatalf("output does not start with %q:\n%s", want, output)
   612  	}
   613  }
   614  
   615  func TestPanicDeadlockGosched(t *testing.T) {
   616  	testPanicDeadlock(t, "GoschedInPanic", "panic: errorThatGosched\n\n")
   617  }
   618  
   619  func TestPanicDeadlockSyscall(t *testing.T) {
   620  	testPanicDeadlock(t, "SyscallInPanic", "1\n2\npanic: 3\n\n")
   621  }
   622  
   623  func TestPanicLoop(t *testing.T) {
   624  	output := runTestProg(t, "testprog", "PanicLoop")
   625  	if want := "panic while printing panic value"; !strings.Contains(output, want) {
   626  		t.Errorf("output does not contain %q:\n%s", want, output)
   627  	}
   628  }
   629  
   630  func TestMemPprof(t *testing.T) {
   631  	testenv.MustHaveGoRun(t)
   632  
   633  	exe, err := buildTestProg(t, "testprog")
   634  	if err != nil {
   635  		t.Fatal(err)
   636  	}
   637  
   638  	got, err := testenv.CleanCmdEnv(exec.Command(exe, "MemProf")).CombinedOutput()
   639  	if err != nil {
   640  		t.Fatalf("testprog failed: %s, output:\n%s", err, got)
   641  	}
   642  	fn := strings.TrimSpace(string(got))
   643  	defer os.Remove(fn)
   644  
   645  	for try := 0; try < 2; try++ {
   646  		cmd := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), "tool", "pprof", "-alloc_space", "-top"))
   647  		// Check that pprof works both with and without explicit executable on command line.
   648  		if try == 0 {
   649  			cmd.Args = append(cmd.Args, exe, fn)
   650  		} else {
   651  			cmd.Args = append(cmd.Args, fn)
   652  		}
   653  		found := false
   654  		for i, e := range cmd.Env {
   655  			if strings.HasPrefix(e, "PPROF_TMPDIR=") {
   656  				cmd.Env[i] = "PPROF_TMPDIR=" + os.TempDir()
   657  				found = true
   658  				break
   659  			}
   660  		}
   661  		if !found {
   662  			cmd.Env = append(cmd.Env, "PPROF_TMPDIR="+os.TempDir())
   663  		}
   664  
   665  		top, err := cmd.CombinedOutput()
   666  		t.Logf("%s:\n%s", cmd.Args, top)
   667  		if err != nil {
   668  			t.Error(err)
   669  		} else if !bytes.Contains(top, []byte("MemProf")) {
   670  			t.Error("missing MemProf in pprof output")
   671  		}
   672  	}
   673  }
   674  
   675  var concurrentMapTest = flag.Bool("run_concurrent_map_tests", false, "also run flaky concurrent map tests")
   676  
   677  func TestConcurrentMapWrites(t *testing.T) {
   678  	if !*concurrentMapTest {
   679  		t.Skip("skipping without -run_concurrent_map_tests")
   680  	}
   681  	if race.Enabled {
   682  		t.Skip("skipping test: -race will catch the race, this test is for the built-in race detection")
   683  	}
   684  	testenv.MustHaveGoRun(t)
   685  	output := runTestProg(t, "testprog", "concurrentMapWrites")
   686  	want := "fatal error: concurrent map writes\n"
   687  	// Concurrent writes can corrupt the map in a way that we
   688  	// detect with a separate throw.
   689  	want2 := "fatal error: small map with no empty slot (concurrent map writes?)\n"
   690  	if !strings.HasPrefix(output, want) && !strings.HasPrefix(output, want2) {
   691  		t.Fatalf("output does not start with %q:\n%s", want, output)
   692  	}
   693  }
   694  func TestConcurrentMapReadWrite(t *testing.T) {
   695  	if !*concurrentMapTest {
   696  		t.Skip("skipping without -run_concurrent_map_tests")
   697  	}
   698  	if race.Enabled {
   699  		t.Skip("skipping test: -race will catch the race, this test is for the built-in race detection")
   700  	}
   701  	testenv.MustHaveGoRun(t)
   702  	output := runTestProg(t, "testprog", "concurrentMapReadWrite")
   703  	want := "fatal error: concurrent map read and map write\n"
   704  	// Concurrent writes can corrupt the map in a way that we
   705  	// detect with a separate throw.
   706  	want2 := "fatal error: small map with no empty slot (concurrent map writes?)\n"
   707  	if !strings.HasPrefix(output, want) && !strings.HasPrefix(output, want2) {
   708  		t.Fatalf("output does not start with %q:\n%s", want, output)
   709  	}
   710  }
   711  func TestConcurrentMapIterateWrite(t *testing.T) {
   712  	if !*concurrentMapTest {
   713  		t.Skip("skipping without -run_concurrent_map_tests")
   714  	}
   715  	if race.Enabled {
   716  		t.Skip("skipping test: -race will catch the race, this test is for the built-in race detection")
   717  	}
   718  	testenv.MustHaveGoRun(t)
   719  	output := runTestProg(t, "testprog", "concurrentMapIterateWrite")
   720  	want := "fatal error: concurrent map iteration and map write\n"
   721  	// Concurrent writes can corrupt the map in a way that we
   722  	// detect with a separate throw.
   723  	want2 := "fatal error: small map with no empty slot (concurrent map writes?)\n"
   724  	if !strings.HasPrefix(output, want) && !strings.HasPrefix(output, want2) {
   725  		t.Fatalf("output does not start with %q:\n%s", want, output)
   726  	}
   727  }
   728  
   729  func TestConcurrentMapWritesIssue69447(t *testing.T) {
   730  	testenv.MustHaveGoRun(t)
   731  	if race.Enabled {
   732  		t.Skip("skipping test: -race will catch the race, this test is for the built-in race detection")
   733  	}
   734  	exe, err := buildTestProg(t, "testprog")
   735  	if err != nil {
   736  		t.Fatal(err)
   737  	}
   738  	for i := 0; i < 200; i++ {
   739  		output := runBuiltTestProg(t, exe, "concurrentMapWrites")
   740  		if output == "" {
   741  			// If we didn't detect an error, that's ok.
   742  			// This case makes this test not flaky like
   743  			// the other ones above.
   744  			// (More correctly, this case makes this test flaky
   745  			// in the other direction, in that it might not
   746  			// detect a problem even if there is one.)
   747  			continue
   748  		}
   749  		want := "fatal error: concurrent map writes\n"
   750  		// Concurrent writes can corrupt the map in a way that we
   751  		// detect with a separate throw.
   752  		want2 := "fatal error: small map with no empty slot (concurrent map writes?)\n"
   753  		if !strings.HasPrefix(output, want) && !strings.HasPrefix(output, want2) {
   754  			t.Fatalf("output does not start with %q:\n%s", want, output)
   755  		}
   756  	}
   757  }
   758  
   759  type point struct {
   760  	x, y *int
   761  }
   762  
   763  func (p *point) negate() {
   764  	*p.x = *p.x * -1
   765  	*p.y = *p.y * -1
   766  }
   767  
   768  // Test for issue #10152.
   769  func TestPanicInlined(t *testing.T) {
   770  	defer func() {
   771  		r := recover()
   772  		if r == nil {
   773  			t.Fatalf("recover failed")
   774  		}
   775  		buf := make([]byte, 2048)
   776  		n := runtime.Stack(buf, false)
   777  		buf = buf[:n]
   778  		if !bytes.Contains(buf, []byte("(*point).negate(")) {
   779  			t.Fatalf("expecting stack trace to contain call to (*point).negate()")
   780  		}
   781  	}()
   782  
   783  	pt := new(point)
   784  	pt.negate()
   785  }
   786  
   787  // Test for issues #3934 and #20018.
   788  // We want to delay exiting until a panic print is complete.
   789  func TestPanicRace(t *testing.T) {
   790  	testenv.MustHaveGoRun(t)
   791  
   792  	exe, err := buildTestProg(t, "testprog")
   793  	if err != nil {
   794  		t.Fatal(err)
   795  	}
   796  
   797  	// The test is intentionally racy, and in my testing does not
   798  	// produce the expected output about 0.05% of the time.
   799  	// So run the program in a loop and only fail the test if we
   800  	// get the wrong output ten times in a row.
   801  	const tries = 10
   802  retry:
   803  	for i := 0; i < tries; i++ {
   804  		got, err := testenv.CleanCmdEnv(exec.Command(exe, "PanicRace")).CombinedOutput()
   805  		if err == nil {
   806  			t.Logf("try %d: program exited successfully, should have failed", i+1)
   807  			continue
   808  		}
   809  
   810  		if i > 0 {
   811  			t.Logf("try %d:\n", i+1)
   812  		}
   813  		t.Logf("%s\n", got)
   814  
   815  		wants := []string{
   816  			"panic: crash",
   817  			"PanicRace",
   818  			"created by ",
   819  		}
   820  		for _, want := range wants {
   821  			if !bytes.Contains(got, []byte(want)) {
   822  				t.Logf("did not find expected string %q", want)
   823  				continue retry
   824  			}
   825  		}
   826  
   827  		// Test generated expected output.
   828  		return
   829  	}
   830  	t.Errorf("test ran %d times without producing expected output", tries)
   831  }
   832  
   833  func TestBadTraceback(t *testing.T) {
   834  	if asan.Enabled || msan.Enabled || race.Enabled {
   835  		t.Skip("skipped test: checkptr mode catches the corruption")
   836  	}
   837  	output := runTestProg(t, "testprog", "BadTraceback")
   838  	for _, want := range []string{
   839  		"unexpected return pc",
   840  		"called from 0xbad",
   841  		"00000bad",    // Smashed LR in hex dump
   842  		"<main.badLR", // Symbolization in hex dump (badLR1 or badLR2)
   843  	} {
   844  		if !strings.Contains(output, want) {
   845  			t.Errorf("output does not contain %q:\n%s", want, output)
   846  		}
   847  	}
   848  }
   849  
   850  func TestTimePprof(t *testing.T) {
   851  	// This test is unreliable on any system in which nanotime
   852  	// calls into libc.
   853  	switch runtime.GOOS {
   854  	case "aix", "darwin", "illumos", "openbsd", "solaris":
   855  		t.Skipf("skipping on %s because nanotime calls libc", runtime.GOOS)
   856  	}
   857  	if race.Enabled || asan.Enabled || msan.Enabled {
   858  		t.Skip("skipping on sanitizers because the sanitizer runtime is external code")
   859  	}
   860  
   861  	// Pass GOTRACEBACK for issue #41120 to try to get more
   862  	// information on timeout.
   863  	fn := runTestProg(t, "testprog", "TimeProf", "GOTRACEBACK=crash")
   864  	fn = strings.TrimSpace(fn)
   865  	defer os.Remove(fn)
   866  
   867  	cmd := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), "tool", "pprof", "-top", "-nodecount=1", fn))
   868  	cmd.Env = append(cmd.Env, "PPROF_TMPDIR="+os.TempDir())
   869  	top, err := cmd.CombinedOutput()
   870  	t.Logf("%s", top)
   871  	if err != nil {
   872  		t.Error(err)
   873  	} else if bytes.Contains(top, []byte("ExternalCode")) {
   874  		t.Error("profiler refers to ExternalCode")
   875  	}
   876  }
   877  
   878  // Test that runtime.abort does so.
   879  func TestAbort(t *testing.T) {
   880  	// Pass GOTRACEBACK to ensure we get runtime frames.
   881  	output := runTestProg(t, "testprog", "Abort", "GOTRACEBACK=system")
   882  	if want := "runtime.abort"; !strings.Contains(output, want) {
   883  		t.Errorf("output does not contain %q:\n%s", want, output)
   884  	}
   885  	if strings.Contains(output, "BAD") {
   886  		t.Errorf("output contains BAD:\n%s", output)
   887  	}
   888  	// Check that it's a signal traceback.
   889  	want := "PC="
   890  	// For systems that use a breakpoint, check specifically for that.
   891  	switch runtime.GOARCH {
   892  	case "386", "amd64":
   893  		switch runtime.GOOS {
   894  		case "plan9":
   895  			want = "sys: breakpoint"
   896  		case "windows":
   897  			want = "Exception 0x80000003"
   898  		default:
   899  			want = "SIGTRAP"
   900  		}
   901  	}
   902  	if !strings.Contains(output, want) {
   903  		t.Errorf("output does not contain %q:\n%s", want, output)
   904  	}
   905  }
   906  
   907  // For TestRuntimePanic: test a panic in the runtime package without
   908  // involving the testing harness.
   909  func init() {
   910  	if os.Getenv("GO_TEST_RUNTIME_PANIC") == "1" {
   911  		defer func() {
   912  			if r := recover(); r != nil {
   913  				// We expect to crash, so exit 0
   914  				// to indicate failure.
   915  				os.Exit(0)
   916  			}
   917  		}()
   918  		runtime.PanicForTesting(nil, 1)
   919  		// We expect to crash, so exit 0 to indicate failure.
   920  		os.Exit(0)
   921  	}
   922  	if os.Getenv("GO_TEST_RUNTIME_NPE_READMEMSTATS") == "1" {
   923  		runtime.ReadMemStats(nil)
   924  		os.Exit(0)
   925  	}
   926  	if os.Getenv("GO_TEST_RUNTIME_NPE_FUNCMETHOD") == "1" {
   927  		var f *runtime.Func
   928  		_ = f.Entry()
   929  		os.Exit(0)
   930  	}
   931  
   932  }
   933  
   934  func TestRuntimePanic(t *testing.T) {
   935  	cmd := testenv.CleanCmdEnv(exec.Command(testenv.Executable(t), "-test.run=^TestRuntimePanic$"))
   936  	cmd.Env = append(cmd.Env, "GO_TEST_RUNTIME_PANIC=1")
   937  	out, err := cmd.CombinedOutput()
   938  	t.Logf("%s", out)
   939  	if err == nil {
   940  		t.Error("child process did not fail")
   941  	} else if want := "runtime.unexportedPanicForTesting"; !bytes.Contains(out, []byte(want)) {
   942  		t.Errorf("output did not contain expected string %q", want)
   943  	}
   944  }
   945  
   946  func TestTracebackRuntimeFunction(t *testing.T) {
   947  	cmd := testenv.CleanCmdEnv(exec.Command(testenv.Executable(t), "-test.run=^TestTracebackRuntimeFunction$"))
   948  	cmd.Env = append(cmd.Env, "GO_TEST_RUNTIME_NPE_READMEMSTATS=1")
   949  	out, err := cmd.CombinedOutput()
   950  	t.Logf("%s", out)
   951  	if err == nil {
   952  		t.Error("child process did not fail")
   953  	} else if want := "runtime.ReadMemStats"; !bytes.Contains(out, []byte(want)) {
   954  		t.Errorf("output did not contain expected string %q", want)
   955  	}
   956  }
   957  
   958  func TestTracebackRuntimeMethod(t *testing.T) {
   959  	cmd := testenv.CleanCmdEnv(exec.Command(testenv.Executable(t), "-test.run=^TestTracebackRuntimeMethod$"))
   960  	cmd.Env = append(cmd.Env, "GO_TEST_RUNTIME_NPE_FUNCMETHOD=1")
   961  	out, err := cmd.CombinedOutput()
   962  	t.Logf("%s", out)
   963  	if err == nil {
   964  		t.Error("child process did not fail")
   965  	} else if want := "runtime.(*Func).Entry"; !bytes.Contains(out, []byte(want)) {
   966  		t.Errorf("output did not contain expected string %q", want)
   967  	}
   968  }
   969  
   970  // Test that g0 stack overflows are handled gracefully.
   971  func TestG0StackOverflow(t *testing.T) {
   972  	if runtime.GOOS == "ios" {
   973  		testenv.SkipFlaky(t, 62671)
   974  	}
   975  
   976  	if os.Getenv("TEST_G0_STACK_OVERFLOW") != "1" {
   977  		cmd := testenv.CleanCmdEnv(testenv.Command(t, testenv.Executable(t), "-test.run=^TestG0StackOverflow$", "-test.v"))
   978  		cmd.Env = append(cmd.Env, "TEST_G0_STACK_OVERFLOW=1")
   979  		out, err := cmd.CombinedOutput()
   980  		t.Logf("output:\n%s", out)
   981  		// Don't check err since it's expected to crash.
   982  		if n := strings.Count(string(out), "morestack on g0\n"); n != 1 {
   983  			t.Fatalf("%s\n(exit status %v)", out, err)
   984  		}
   985  		if runtime.CrashStackImplemented {
   986  			// check for a stack trace
   987  			want := "runtime.stackOverflow"
   988  			if n := strings.Count(string(out), want); n < 5 {
   989  				t.Errorf("output does not contain %q at least 5 times:\n%s", want, out)
   990  			}
   991  			return // it's not a signal-style traceback
   992  		}
   993  		// Check that it's a signal-style traceback.
   994  		if runtime.GOOS != "windows" {
   995  			if want := "PC="; !strings.Contains(string(out), want) {
   996  				t.Errorf("output does not contain %q:\n%s", want, out)
   997  			}
   998  		}
   999  		return
  1000  	}
  1001  
  1002  	runtime.G0StackOverflow()
  1003  }
  1004  
  1005  // For TestCrashWhileTracing: test a panic without involving the testing
  1006  // harness, as we rely on stdout only containing trace output.
  1007  func init() {
  1008  	if os.Getenv("TEST_CRASH_WHILE_TRACING") == "1" {
  1009  		trace.Start(os.Stdout)
  1010  		trace.Log(context.Background(), "xyzzy-cat", "xyzzy-msg")
  1011  		panic("yzzyx")
  1012  	}
  1013  }
  1014  
  1015  func TestCrashWhileTracing(t *testing.T) {
  1016  	testenv.MustHaveExec(t)
  1017  
  1018  	cmd := testenv.CleanCmdEnv(testenv.Command(t, testenv.Executable(t)))
  1019  	cmd.Env = append(cmd.Env, "TEST_CRASH_WHILE_TRACING=1")
  1020  	stdOut, err := cmd.StdoutPipe()
  1021  	var errOut bytes.Buffer
  1022  	cmd.Stderr = &errOut
  1023  
  1024  	if err := cmd.Start(); err != nil {
  1025  		t.Fatalf("could not start subprocess: %v", err)
  1026  	}
  1027  	r, err := traceparse.NewReader(stdOut)
  1028  	if err != nil {
  1029  		t.Fatalf("could not create trace.NewReader: %v", err)
  1030  	}
  1031  	var seen bool
  1032  	nSync := 0
  1033  	i := 1
  1034  loop:
  1035  	for ; ; i++ {
  1036  		ev, err := r.ReadEvent()
  1037  		if err != nil {
  1038  			// We may have a broken tail to the trace -- that's OK.
  1039  			// We'll make sure we saw at least one complete generation.
  1040  			if err != io.EOF {
  1041  				t.Logf("error at event %d: %v", i, err)
  1042  			}
  1043  			break loop
  1044  		}
  1045  		switch ev.Kind() {
  1046  		case traceparse.EventSync:
  1047  			nSync = ev.Sync().N
  1048  		case traceparse.EventLog:
  1049  			v := ev.Log()
  1050  			if v.Category == "xyzzy-cat" && v.Message == "xyzzy-msg" {
  1051  				// Should we already stop reading here? More events may come, but
  1052  				// we're not guaranteeing a fully unbroken trace until the last
  1053  				// byte...
  1054  				seen = true
  1055  			}
  1056  		}
  1057  	}
  1058  	if err := cmd.Wait(); err == nil {
  1059  		t.Error("the process should have panicked")
  1060  	}
  1061  	if nSync <= 1 {
  1062  		t.Errorf("expected at least one full generation to have been emitted before the trace was considered broken")
  1063  	}
  1064  	if !seen {
  1065  		t.Errorf("expected one matching log event matching, but none of the %d received trace events match", i)
  1066  	}
  1067  	t.Logf("stderr output:\n%s", errOut.String())
  1068  	needle := "yzzyx\n"
  1069  	if n := strings.Count(errOut.String(), needle); n != 1 {
  1070  		t.Fatalf("did not find expected panic message %q\n(exit status %v)", needle, err)
  1071  	}
  1072  }
  1073  
  1074  // Test that panic message is not clobbered.
  1075  // See issue 30150.
  1076  func TestDoublePanic(t *testing.T) {
  1077  	output := runTestProg(t, "testprog", "DoublePanic", "GODEBUG=clobberfree=1")
  1078  	wants := []string{"panic: XXX", "panic: YYY"}
  1079  	for _, want := range wants {
  1080  		if !strings.Contains(output, want) {
  1081  			t.Errorf("output:\n%s\n\nwant output containing: %s", output, want)
  1082  		}
  1083  	}
  1084  }
  1085  
  1086  // Test that panic while panicking discards error message
  1087  // See issue 52257
  1088  func TestPanicWhilePanicking(t *testing.T) {
  1089  	tests := []struct {
  1090  		Want string
  1091  		Func string
  1092  	}{
  1093  		{
  1094  			"panic while printing panic value: important multi-line\n\terror message",
  1095  			"ErrorPanic",
  1096  		},
  1097  		{
  1098  			"panic while printing panic value: important multi-line\n\tstringer message",
  1099  			"StringerPanic",
  1100  		},
  1101  		{
  1102  			"panic while printing panic value: type",
  1103  			"DoubleErrorPanic",
  1104  		},
  1105  		{
  1106  			"panic while printing panic value: type",
  1107  			"DoubleStringerPanic",
  1108  		},
  1109  		{
  1110  			"panic while printing panic value: type",
  1111  			"CircularPanic",
  1112  		},
  1113  		{
  1114  			"important multi-line\n\tstring message",
  1115  			"StringPanic",
  1116  		},
  1117  		{
  1118  			"nil",
  1119  			"NilPanic",
  1120  		},
  1121  	}
  1122  	for _, x := range tests {
  1123  		output := runTestProg(t, "testprog", x.Func)
  1124  		if !strings.Contains(output, x.Want) {
  1125  			t.Errorf("output does not contain %q:\n%s", x.Want, output)
  1126  		}
  1127  	}
  1128  }
  1129  
  1130  func TestPanicOnUnsafeSlice(t *testing.T) {
  1131  	output := runTestProg(t, "testprog", "panicOnNilAndEleSizeIsZero")
  1132  	// Note: This is normally a panic, but is a throw when checkptr is
  1133  	// enabled.
  1134  	want := "unsafe.Slice: ptr is nil and len is not zero"
  1135  	if !strings.Contains(output, want) {
  1136  		t.Errorf("output does not contain %q:\n%s", want, output)
  1137  	}
  1138  }
  1139  
  1140  func TestNetpollWaiters(t *testing.T) {
  1141  	t.Parallel()
  1142  	output := runTestProg(t, "testprognet", "NetpollWaiters")
  1143  	want := "OK\n"
  1144  	if output != want {
  1145  		t.Fatalf("output is not %q\n%s", want, output)
  1146  	}
  1147  }
  1148  
  1149  func TestFinalizerOrCleanupDeadlock(t *testing.T) {
  1150  	t.Parallel()
  1151  
  1152  	for _, useCleanup := range []bool{false, true} {
  1153  		progName := "Finalizer"
  1154  		want := "runtime.runFinalizers"
  1155  		if useCleanup {
  1156  			progName = "Cleanup"
  1157  			want = "runtime.runCleanups"
  1158  		}
  1159  
  1160  		// The runtime.runFinalizers/runtime.runCleanups frame should appear in panics, even if
  1161  		// runtime frames are normally hidden (GOTRACEBACK=all).
  1162  		t.Run("Panic", func(t *testing.T) {
  1163  			t.Parallel()
  1164  			output := runTestProg(t, "testprog", progName+"Deadlock", "GOTRACEBACK=all", "GO_TEST_FINALIZER_DEADLOCK=panic")
  1165  			want := want + "()"
  1166  			if !strings.Contains(output, want) {
  1167  				t.Errorf("output does not contain %q:\n%s", want, output)
  1168  			}
  1169  		})
  1170  
  1171  		// The runtime.runFinalizers/runtime.Cleanups frame should appear in runtime.Stack,
  1172  		// even though runtime frames are normally hidden.
  1173  		t.Run("Stack", func(t *testing.T) {
  1174  			t.Parallel()
  1175  			output := runTestProg(t, "testprog", progName+"Deadlock", "GO_TEST_FINALIZER_DEADLOCK=stack")
  1176  			want := want + "()"
  1177  			if !strings.Contains(output, want) {
  1178  				t.Errorf("output does not contain %q:\n%s", want, output)
  1179  			}
  1180  		})
  1181  
  1182  		// The runtime.runFinalizers/runtime.Cleanups frame should appear in goroutine
  1183  		// profiles.
  1184  		t.Run("PprofProto", func(t *testing.T) {
  1185  			t.Parallel()
  1186  			output := runTestProg(t, "testprog", progName+"Deadlock", "GO_TEST_FINALIZER_DEADLOCK=pprof_proto")
  1187  
  1188  			p, err := profile.Parse(strings.NewReader(output))
  1189  			if err != nil {
  1190  				// Logging the binary proto data is not very nice, but it might
  1191  				// be a text error message instead.
  1192  				t.Logf("Output: %s", output)
  1193  				t.Fatalf("Error parsing proto output: %v", err)
  1194  			}
  1195  			for _, s := range p.Sample {
  1196  				for _, loc := range s.Location {
  1197  					for _, line := range loc.Line {
  1198  						if line.Function.Name == want {
  1199  							// Done!
  1200  							return
  1201  						}
  1202  					}
  1203  				}
  1204  			}
  1205  			t.Errorf("Profile does not contain %q:\n%s", want, p)
  1206  		})
  1207  
  1208  		// The runtime.runFinalizers/runtime.runCleanups frame should appear in goroutine
  1209  		// profiles (debug=1).
  1210  		t.Run("PprofDebug1", func(t *testing.T) {
  1211  			t.Parallel()
  1212  			output := runTestProg(t, "testprog", progName+"Deadlock", "GO_TEST_FINALIZER_DEADLOCK=pprof_debug1")
  1213  			want := want + "+"
  1214  			if !strings.Contains(output, want) {
  1215  				t.Errorf("output does not contain %q:\n%s", want, output)
  1216  			}
  1217  		})
  1218  
  1219  		// The runtime.runFinalizers/runtime.runCleanups frame should appear in goroutine
  1220  		// profiles (debug=2).
  1221  		t.Run("PprofDebug2", func(t *testing.T) {
  1222  			t.Parallel()
  1223  			output := runTestProg(t, "testprog", progName+"Deadlock", "GO_TEST_FINALIZER_DEADLOCK=pprof_debug2")
  1224  			want := want + "()"
  1225  			if !strings.Contains(output, want) {
  1226  				t.Errorf("output does not contain %q:\n%s", want, output)
  1227  			}
  1228  		})
  1229  	}
  1230  }
  1231  
  1232  func TestSynctestCondSignalFromNoBubble(t *testing.T) {
  1233  	for _, test := range []string{
  1234  		"SynctestCond/signal/no_bubble",
  1235  		"SynctestCond/broadcast/no_bubble",
  1236  		"SynctestCond/signal/other_bubble",
  1237  		"SynctestCond/broadcast/other_bubble",
  1238  	} {
  1239  		t.Run(test, func(t *testing.T) {
  1240  			output := runTestProg(t, "testprog", test)
  1241  			want := "fatal error: semaphore wake of synctest goroutine from outside bubble"
  1242  			if !strings.Contains(output, want) {
  1243  				t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
  1244  			}
  1245  		})
  1246  	}
  1247  }
  1248  

View as plain text