Source file src/testing/testing_test.go

     1  // Copyright 2014 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  	"bytes"
     9  	"context"
    10  	"errors"
    11  	"fmt"
    12  	"internal/race"
    13  	"internal/testenv"
    14  	"os"
    15  	"os/exec"
    16  	"path/filepath"
    17  	"regexp"
    18  	"runtime"
    19  	"slices"
    20  	"strings"
    21  	"sync"
    22  	"testing"
    23  	"time"
    24  )
    25  
    26  // This is exactly what a test would do without a TestMain.
    27  // It's here only so that there is at least one package in the
    28  // standard library with a TestMain, so that code is executed.
    29  
    30  func TestMain(m *testing.M) {
    31  	if os.Getenv("GO_WANT_RACE_BEFORE_TESTS") == "1" {
    32  		doRace()
    33  	}
    34  
    35  	m.Run()
    36  
    37  	// Note: m.Run currently prints the final "PASS" line, so if any race is
    38  	// reported here (after m.Run but before the process exits), it will print
    39  	// "PASS", then print the stack traces for the race, then exit with nonzero
    40  	// status.
    41  	//
    42  	// This is a somewhat fundamental race: because the race detector hooks into
    43  	// the runtime at a very low level, no matter where we put the printing it
    44  	// would be possible to report a race that occurs afterward. However, we could
    45  	// theoretically move the printing after TestMain, which would at least do a
    46  	// better job of diagnosing races in cleanup functions within TestMain itself.
    47  }
    48  
    49  func TestTempDirInCleanup(t *testing.T) {
    50  	var dir string
    51  
    52  	t.Run("test", func(t *testing.T) {
    53  		t.Cleanup(func() {
    54  			dir = t.TempDir()
    55  		})
    56  		_ = t.TempDir()
    57  	})
    58  
    59  	fi, err := os.Stat(dir)
    60  	if fi != nil {
    61  		t.Fatalf("Directory %q from user Cleanup still exists", dir)
    62  	}
    63  	if !os.IsNotExist(err) {
    64  		t.Fatalf("Unexpected error: %v", err)
    65  	}
    66  }
    67  
    68  func TestTempDirInBenchmark(t *testing.T) {
    69  	testing.Benchmark(func(b *testing.B) {
    70  		if !b.Run("test", func(b *testing.B) {
    71  			// Add a loop so that the test won't fail. See issue 38677.
    72  			for i := 0; i < b.N; i++ {
    73  				_ = b.TempDir()
    74  			}
    75  		}) {
    76  			t.Fatal("Sub test failure in a benchmark")
    77  		}
    78  	})
    79  }
    80  
    81  func TestTempDir(t *testing.T) {
    82  	testTempDir(t)
    83  	t.Run("InSubtest", testTempDir)
    84  	t.Run("test/subtest", testTempDir)
    85  	t.Run("test\\subtest", testTempDir)
    86  	t.Run("test:subtest", testTempDir)
    87  	t.Run("test/..", testTempDir)
    88  	t.Run("../test", testTempDir)
    89  	t.Run("test[]", testTempDir)
    90  	t.Run("test*", testTempDir)
    91  	t.Run("äöüéè", testTempDir)
    92  }
    93  
    94  func testTempDir(t *testing.T) {
    95  	dirCh := make(chan string, 1)
    96  	t.Cleanup(func() {
    97  		// Verify directory has been removed.
    98  		select {
    99  		case dir := <-dirCh:
   100  			fi, err := os.Stat(dir)
   101  			if os.IsNotExist(err) {
   102  				// All good
   103  				return
   104  			}
   105  			if err != nil {
   106  				t.Fatal(err)
   107  			}
   108  			t.Errorf("directory %q still exists: %v, isDir=%v", dir, fi, fi.IsDir())
   109  		default:
   110  			if !t.Failed() {
   111  				t.Fatal("never received dir channel")
   112  			}
   113  		}
   114  	})
   115  
   116  	dir := t.TempDir()
   117  	if dir == "" {
   118  		t.Fatal("expected dir")
   119  	}
   120  	dir2 := t.TempDir()
   121  	if dir == dir2 {
   122  		t.Fatal("subsequent calls to TempDir returned the same directory")
   123  	}
   124  	if filepath.Dir(dir) != filepath.Dir(dir2) {
   125  		t.Fatalf("calls to TempDir do not share a parent; got %q, %q", dir, dir2)
   126  	}
   127  	dirCh <- dir
   128  	fi, err := os.Stat(dir)
   129  	if err != nil {
   130  		t.Fatal(err)
   131  	}
   132  	if !fi.IsDir() {
   133  		t.Errorf("dir %q is not a dir", dir)
   134  	}
   135  	files, err := os.ReadDir(dir)
   136  	if err != nil {
   137  		t.Fatal(err)
   138  	}
   139  	if len(files) > 0 {
   140  		t.Errorf("unexpected %d files in TempDir: %v", len(files), files)
   141  	}
   142  
   143  	glob := filepath.Join(dir, "*.txt")
   144  	if _, err := filepath.Glob(glob); err != nil {
   145  		t.Error(err)
   146  	}
   147  }
   148  
   149  func TestSetenv(t *testing.T) {
   150  	tests := []struct {
   151  		name               string
   152  		key                string
   153  		initialValueExists bool
   154  		initialValue       string
   155  		newValue           string
   156  	}{
   157  		{
   158  			name:               "initial value exists",
   159  			key:                "GO_TEST_KEY_1",
   160  			initialValueExists: true,
   161  			initialValue:       "111",
   162  			newValue:           "222",
   163  		},
   164  		{
   165  			name:               "initial value exists but empty",
   166  			key:                "GO_TEST_KEY_2",
   167  			initialValueExists: true,
   168  			initialValue:       "",
   169  			newValue:           "222",
   170  		},
   171  		{
   172  			name:               "initial value is not exists",
   173  			key:                "GO_TEST_KEY_3",
   174  			initialValueExists: false,
   175  			initialValue:       "",
   176  			newValue:           "222",
   177  		},
   178  	}
   179  
   180  	for _, test := range tests {
   181  		if test.initialValueExists {
   182  			if err := os.Setenv(test.key, test.initialValue); err != nil {
   183  				t.Fatalf("unable to set env: got %v", err)
   184  			}
   185  		} else {
   186  			os.Unsetenv(test.key)
   187  		}
   188  
   189  		t.Run(test.name, func(t *testing.T) {
   190  			t.Setenv(test.key, test.newValue)
   191  			if os.Getenv(test.key) != test.newValue {
   192  				t.Fatalf("unexpected value after t.Setenv: got %s, want %s", os.Getenv(test.key), test.newValue)
   193  			}
   194  		})
   195  
   196  		got, exists := os.LookupEnv(test.key)
   197  		if got != test.initialValue {
   198  			t.Fatalf("unexpected value after t.Setenv cleanup: got %s, want %s", got, test.initialValue)
   199  		}
   200  		if exists != test.initialValueExists {
   201  			t.Fatalf("unexpected value after t.Setenv cleanup: got %t, want %t", exists, test.initialValueExists)
   202  		}
   203  	}
   204  }
   205  
   206  func expectParallelConflict(t *testing.T) {
   207  	want := testing.ParallelConflict
   208  	if got := recover(); got != want {
   209  		t.Fatalf("expected panic; got %#v want %q", got, want)
   210  	}
   211  }
   212  
   213  func testWithParallelAfter(t *testing.T, fn func(*testing.T)) {
   214  	defer expectParallelConflict(t)
   215  
   216  	fn(t)
   217  	t.Parallel()
   218  }
   219  
   220  func testWithParallelBefore(t *testing.T, fn func(*testing.T)) {
   221  	defer expectParallelConflict(t)
   222  
   223  	t.Parallel()
   224  	fn(t)
   225  }
   226  
   227  func testWithParallelParentBefore(t *testing.T, fn func(*testing.T)) {
   228  	t.Parallel()
   229  
   230  	t.Run("child", func(t *testing.T) {
   231  		defer expectParallelConflict(t)
   232  
   233  		fn(t)
   234  	})
   235  }
   236  
   237  func testWithParallelGrandParentBefore(t *testing.T, fn func(*testing.T)) {
   238  	t.Parallel()
   239  
   240  	t.Run("child", func(t *testing.T) {
   241  		t.Run("grand-child", func(t *testing.T) {
   242  			defer expectParallelConflict(t)
   243  
   244  			fn(t)
   245  		})
   246  	})
   247  }
   248  
   249  func tSetenv(t *testing.T) {
   250  	t.Setenv("GO_TEST_KEY_1", "value")
   251  }
   252  
   253  func TestSetenvWithParallelAfter(t *testing.T) {
   254  	testWithParallelAfter(t, tSetenv)
   255  }
   256  
   257  func TestSetenvWithParallelBefore(t *testing.T) {
   258  	testWithParallelBefore(t, tSetenv)
   259  }
   260  
   261  func TestSetenvWithParallelParentBefore(t *testing.T) {
   262  	testWithParallelParentBefore(t, tSetenv)
   263  }
   264  
   265  func TestSetenvWithParallelGrandParentBefore(t *testing.T) {
   266  	testWithParallelGrandParentBefore(t, tSetenv)
   267  }
   268  
   269  func tChdir(t *testing.T) {
   270  	t.Chdir(t.TempDir())
   271  }
   272  
   273  func TestChdirWithParallelAfter(t *testing.T) {
   274  	testWithParallelAfter(t, tChdir)
   275  }
   276  
   277  func TestChdirWithParallelBefore(t *testing.T) {
   278  	testWithParallelBefore(t, tChdir)
   279  }
   280  
   281  func TestChdirWithParallelParentBefore(t *testing.T) {
   282  	testWithParallelParentBefore(t, tChdir)
   283  }
   284  
   285  func TestChdirWithParallelGrandParentBefore(t *testing.T) {
   286  	testWithParallelGrandParentBefore(t, tChdir)
   287  }
   288  
   289  func TestChdir(t *testing.T) {
   290  	oldDir, err := os.Getwd()
   291  	if err != nil {
   292  		t.Fatal(err)
   293  	}
   294  	defer os.Chdir(oldDir)
   295  
   296  	// The "relative" test case relies on tmp not being a symlink.
   297  	tmp, err := filepath.EvalSymlinks(t.TempDir())
   298  	if err != nil {
   299  		t.Fatal(err)
   300  	}
   301  	rel, err := filepath.Rel(oldDir, tmp)
   302  	if err != nil {
   303  		// If GOROOT is on C: volume and tmp is on the D: volume, there
   304  		// is no relative path between them, so skip that test case.
   305  		rel = "skip"
   306  	}
   307  
   308  	for _, tc := range []struct {
   309  		name, dir, pwd string
   310  		extraChdir     bool
   311  	}{
   312  		{
   313  			name: "absolute",
   314  			dir:  tmp,
   315  			pwd:  tmp,
   316  		},
   317  		{
   318  			name: "relative",
   319  			dir:  rel,
   320  			pwd:  tmp,
   321  		},
   322  		{
   323  			name: "current (absolute)",
   324  			dir:  oldDir,
   325  			pwd:  oldDir,
   326  		},
   327  		{
   328  			name: "current (relative) with extra os.Chdir",
   329  			dir:  ".",
   330  			pwd:  oldDir,
   331  
   332  			extraChdir: true,
   333  		},
   334  	} {
   335  		t.Run(tc.name, func(t *testing.T) {
   336  			if tc.dir == "skip" {
   337  				t.Skipf("skipping test because there is no relative path between %s and %s", oldDir, tmp)
   338  			}
   339  			if !filepath.IsAbs(tc.pwd) {
   340  				t.Fatalf("Bad tc.pwd: %q (must be absolute)", tc.pwd)
   341  			}
   342  
   343  			t.Chdir(tc.dir)
   344  
   345  			newDir, err := os.Getwd()
   346  			if err != nil {
   347  				t.Fatal(err)
   348  			}
   349  			if newDir != tc.pwd {
   350  				t.Fatalf("failed to chdir to %q: getwd: got %q, want %q", tc.dir, newDir, tc.pwd)
   351  			}
   352  
   353  			switch runtime.GOOS {
   354  			case "windows", "plan9":
   355  				// Windows and Plan 9 do not use the PWD variable.
   356  			default:
   357  				if pwd := os.Getenv("PWD"); pwd != tc.pwd {
   358  					t.Fatalf("PWD: got %q, want %q", pwd, tc.pwd)
   359  				}
   360  			}
   361  
   362  			if tc.extraChdir {
   363  				os.Chdir("..")
   364  			}
   365  		})
   366  
   367  		newDir, err := os.Getwd()
   368  		if err != nil {
   369  			t.Fatal(err)
   370  		}
   371  		if newDir != oldDir {
   372  			t.Fatalf("failed to restore wd to %s: getwd: %s", oldDir, newDir)
   373  		}
   374  	}
   375  }
   376  
   377  // testingTrueInInit is part of TestTesting.
   378  var testingTrueInInit = false
   379  
   380  // testingTrueInPackageVarInit is part of TestTesting.
   381  var testingTrueInPackageVarInit = testing.Testing()
   382  
   383  // init is part of TestTesting.
   384  func init() {
   385  	if testing.Testing() {
   386  		testingTrueInInit = true
   387  	}
   388  }
   389  
   390  var testingProg = `
   391  package main
   392  
   393  import (
   394  	"fmt"
   395  	"testing"
   396  )
   397  
   398  func main() {
   399  	fmt.Println(testing.Testing())
   400  }
   401  `
   402  
   403  func TestTesting(t *testing.T) {
   404  	if !testing.Testing() {
   405  		t.Errorf("testing.Testing() == %t, want %t", testing.Testing(), true)
   406  	}
   407  	if !testingTrueInInit {
   408  		t.Errorf("testing.Testing() called by init function == %t, want %t", testingTrueInInit, true)
   409  	}
   410  	if !testingTrueInPackageVarInit {
   411  		t.Errorf("testing.Testing() variable initialized as %t, want %t", testingTrueInPackageVarInit, true)
   412  	}
   413  
   414  	if testing.Short() {
   415  		t.Skip("skipping building a binary in short mode")
   416  	}
   417  	testenv.MustHaveGoRun(t)
   418  
   419  	fn := filepath.Join(t.TempDir(), "x.go")
   420  	if err := os.WriteFile(fn, []byte(testingProg), 0644); err != nil {
   421  		t.Fatal(err)
   422  	}
   423  
   424  	cmd := testenv.Command(t, testenv.GoToolPath(t), "run", fn)
   425  	out, err := cmd.CombinedOutput()
   426  	if err != nil {
   427  		t.Fatalf("%v failed: %v\n%s", cmd, err, out)
   428  	}
   429  
   430  	s := string(bytes.TrimSpace(out))
   431  	if s != "false" {
   432  		t.Errorf("in non-test testing.Test() returned %q, want %q", s, "false")
   433  	}
   434  }
   435  
   436  // runTest runs a helper test with -test.v, ignoring its exit status.
   437  // runTest both logs and returns the test output.
   438  func runTest(t *testing.T, test string) []byte {
   439  	t.Helper()
   440  
   441  	testenv.MustHaveExec(t)
   442  
   443  	cmd := testenv.Command(t, testenv.Executable(t), "-test.run=^"+test+"$", "-test.bench="+test, "-test.v", "-test.parallel=2", "-test.benchtime=2x")
   444  	cmd = testenv.CleanCmdEnv(cmd)
   445  	cmd.Env = append(cmd.Env, "GO_WANT_HELPER_PROCESS=1")
   446  	out, err := cmd.CombinedOutput()
   447  	t.Logf("%v: %v\n%s", cmd, err, out)
   448  
   449  	return out
   450  }
   451  
   452  // doRace provokes a data race that generates a race detector report if run
   453  // under the race detector and is otherwise benign.
   454  func doRace() {
   455  	var x int
   456  	c1 := make(chan bool)
   457  	go func() {
   458  		x = 1 // racy write
   459  		c1 <- true
   460  	}()
   461  	_ = x // racy read
   462  	<-c1
   463  }
   464  
   465  func TestRaceReports(t *testing.T) {
   466  	if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
   467  		// Generate a race detector report in a sub test.
   468  		t.Run("Sub", func(t *testing.T) {
   469  			doRace()
   470  		})
   471  		return
   472  	}
   473  
   474  	out := runTest(t, "TestRaceReports")
   475  
   476  	// We should see at most one race detector report.
   477  	c := bytes.Count(out, []byte("race detected"))
   478  	want := 0
   479  	if race.Enabled {
   480  		want = 1
   481  	}
   482  	if c != want {
   483  		t.Errorf("got %d race reports, want %d", c, want)
   484  	}
   485  }
   486  
   487  // Issue #60083. This used to fail on the race builder.
   488  func TestRaceName(t *testing.T) {
   489  	if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
   490  		doRace()
   491  		return
   492  	}
   493  
   494  	out := runTest(t, "TestRaceName")
   495  
   496  	if regexp.MustCompile(`=== NAME\s*$`).Match(out) {
   497  		t.Errorf("incorrectly reported test with no name")
   498  	}
   499  }
   500  
   501  func TestRaceSubReports(t *testing.T) {
   502  	if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
   503  		t.Parallel()
   504  		c1 := make(chan bool, 1)
   505  		t.Run("sub", func(t *testing.T) {
   506  			t.Run("subsub1", func(t *testing.T) {
   507  				t.Parallel()
   508  				doRace()
   509  				c1 <- true
   510  			})
   511  			t.Run("subsub2", func(t *testing.T) {
   512  				t.Parallel()
   513  				doRace()
   514  				<-c1
   515  			})
   516  		})
   517  		doRace()
   518  		return
   519  	}
   520  
   521  	out := runTest(t, "TestRaceSubReports")
   522  
   523  	// There should be three race reports: one for each subtest, and one for the
   524  	// race after the subtests complete. Note that because the subtests run in
   525  	// parallel, the race stacks may both be printed in with one or the other
   526  	// test's logs.
   527  	cReport := bytes.Count(out, []byte("race detected during execution of test"))
   528  	wantReport := 0
   529  	if race.Enabled {
   530  		wantReport = 3
   531  	}
   532  	if cReport != wantReport {
   533  		t.Errorf("got %d race reports, want %d", cReport, wantReport)
   534  	}
   535  
   536  	// Regardless of when the stacks are printed, we expect each subtest to be
   537  	// marked as failed, and that failure should propagate up to the parents.
   538  	cFail := bytes.Count(out, []byte("--- FAIL:"))
   539  	wantFail := 0
   540  	if race.Enabled {
   541  		wantFail = 4
   542  	}
   543  	if cFail != wantFail {
   544  		t.Errorf(`got %d "--- FAIL:" lines, want %d`, cReport, wantReport)
   545  	}
   546  }
   547  
   548  func TestRaceInCleanup(t *testing.T) {
   549  	if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
   550  		t.Cleanup(doRace)
   551  		t.Parallel()
   552  		t.Run("sub", func(t *testing.T) {
   553  			t.Parallel()
   554  			// No race should be reported for sub.
   555  		})
   556  		return
   557  	}
   558  
   559  	out := runTest(t, "TestRaceInCleanup")
   560  
   561  	// There should be one race report, for the parent test only.
   562  	cReport := bytes.Count(out, []byte("race detected during execution of test"))
   563  	wantReport := 0
   564  	if race.Enabled {
   565  		wantReport = 1
   566  	}
   567  	if cReport != wantReport {
   568  		t.Errorf("got %d race reports, want %d", cReport, wantReport)
   569  	}
   570  
   571  	// Only the parent test should be marked as failed.
   572  	// (The subtest does not race, and should pass.)
   573  	cFail := bytes.Count(out, []byte("--- FAIL:"))
   574  	wantFail := 0
   575  	if race.Enabled {
   576  		wantFail = 1
   577  	}
   578  	if cFail != wantFail {
   579  		t.Errorf(`got %d "--- FAIL:" lines, want %d`, cReport, wantReport)
   580  	}
   581  }
   582  
   583  func TestDeepSubtestRace(t *testing.T) {
   584  	if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
   585  		t.Run("sub", func(t *testing.T) {
   586  			t.Run("subsub", func(t *testing.T) {
   587  				t.Run("subsubsub", func(t *testing.T) {
   588  					doRace()
   589  				})
   590  			})
   591  			doRace()
   592  		})
   593  		return
   594  	}
   595  
   596  	out := runTest(t, "TestDeepSubtestRace")
   597  
   598  	c := bytes.Count(out, []byte("race detected during execution of test"))
   599  	want := 0
   600  	// There should be two race reports.
   601  	if race.Enabled {
   602  		want = 2
   603  	}
   604  	if c != want {
   605  		t.Errorf("got %d race reports, want %d", c, want)
   606  	}
   607  }
   608  
   609  func TestRaceDuringParallelFailsAllSubtests(t *testing.T) {
   610  	if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
   611  		var ready sync.WaitGroup
   612  		ready.Add(2)
   613  		done := make(chan struct{})
   614  		go func() {
   615  			ready.Wait()
   616  			doRace() // This race happens while both subtests are running.
   617  			close(done)
   618  		}()
   619  
   620  		t.Run("sub", func(t *testing.T) {
   621  			t.Run("subsub1", func(t *testing.T) {
   622  				t.Parallel()
   623  				ready.Done()
   624  				<-done
   625  			})
   626  			t.Run("subsub2", func(t *testing.T) {
   627  				t.Parallel()
   628  				ready.Done()
   629  				<-done
   630  			})
   631  		})
   632  
   633  		return
   634  	}
   635  
   636  	out := runTest(t, "TestRaceDuringParallelFailsAllSubtests")
   637  
   638  	c := bytes.Count(out, []byte("race detected during execution of test"))
   639  	want := 0
   640  	// Each subtest should report the race independently.
   641  	if race.Enabled {
   642  		want = 2
   643  	}
   644  	if c != want {
   645  		t.Errorf("got %d race reports, want %d", c, want)
   646  	}
   647  }
   648  
   649  func TestRaceBeforeParallel(t *testing.T) {
   650  	if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
   651  		t.Run("sub", func(t *testing.T) {
   652  			doRace()
   653  			t.Parallel()
   654  		})
   655  		return
   656  	}
   657  
   658  	out := runTest(t, "TestRaceBeforeParallel")
   659  
   660  	c := bytes.Count(out, []byte("race detected during execution of test"))
   661  	want := 0
   662  	// We should see one race detector report.
   663  	if race.Enabled {
   664  		want = 1
   665  	}
   666  	if c != want {
   667  		t.Errorf("got %d race reports, want %d", c, want)
   668  	}
   669  }
   670  
   671  func TestRaceBeforeTests(t *testing.T) {
   672  	cmd := testenv.Command(t, testenv.Executable(t), "-test.run=^$")
   673  	cmd = testenv.CleanCmdEnv(cmd)
   674  	cmd.Env = append(cmd.Env, "GO_WANT_RACE_BEFORE_TESTS=1")
   675  	out, _ := cmd.CombinedOutput()
   676  	t.Logf("%s", out)
   677  
   678  	c := bytes.Count(out, []byte("race detected outside of test execution"))
   679  
   680  	want := 0
   681  	if race.Enabled {
   682  		want = 1
   683  	}
   684  	if c != want {
   685  		t.Errorf("got %d race reports; want %d", c, want)
   686  	}
   687  }
   688  
   689  func TestBenchmarkRace(t *testing.T) {
   690  	out := runTest(t, "BenchmarkRacy")
   691  	c := bytes.Count(out, []byte("race detected during execution of test"))
   692  
   693  	want := 0
   694  	// We should see one race detector report.
   695  	if race.Enabled {
   696  		want = 1
   697  	}
   698  	if c != want {
   699  		t.Errorf("got %d race reports; want %d", c, want)
   700  	}
   701  }
   702  
   703  func BenchmarkRacy(b *testing.B) {
   704  	if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
   705  		b.Skipf("skipping intentionally-racy benchmark")
   706  	}
   707  	for i := 0; i < b.N; i++ {
   708  		doRace()
   709  	}
   710  }
   711  
   712  func TestBenchmarkSubRace(t *testing.T) {
   713  	out := runTest(t, "BenchmarkSubRacy")
   714  	c := bytes.Count(out, []byte("race detected during execution of test"))
   715  
   716  	want := 0
   717  	// We should see two race detector reports:
   718  	// one in the sub-bencmark, and one in the parent afterward.
   719  	if race.Enabled {
   720  		want = 2
   721  	}
   722  	if c != want {
   723  		t.Errorf("got %d race reports; want %d", c, want)
   724  	}
   725  }
   726  
   727  func BenchmarkSubRacy(b *testing.B) {
   728  	if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
   729  		b.Skipf("skipping intentionally-racy benchmark")
   730  	}
   731  
   732  	b.Run("non-racy", func(b *testing.B) {
   733  		tot := 0
   734  		for i := 0; i < b.N; i++ {
   735  			tot++
   736  		}
   737  		_ = tot
   738  	})
   739  
   740  	b.Run("racy", func(b *testing.B) {
   741  		for i := 0; i < b.N; i++ {
   742  			doRace()
   743  		}
   744  	})
   745  
   746  	doRace() // should be reported separately
   747  }
   748  
   749  func TestRunningTests(t *testing.T) {
   750  	t.Parallel()
   751  
   752  	// Regression test for https://go.dev/issue/64404:
   753  	// on timeout, the "running tests" message should not include
   754  	// tests that are waiting on parked subtests.
   755  
   756  	if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
   757  		for i := 0; i < 2; i++ {
   758  			t.Run(fmt.Sprintf("outer%d", i), func(t *testing.T) {
   759  				t.Parallel()
   760  				for j := 0; j < 2; j++ {
   761  					t.Run(fmt.Sprintf("inner%d", j), func(t *testing.T) {
   762  						t.Parallel()
   763  						for {
   764  							time.Sleep(1 * time.Millisecond)
   765  						}
   766  					})
   767  				}
   768  			})
   769  		}
   770  	}
   771  
   772  	timeout := 10 * time.Millisecond
   773  	for {
   774  		cmd := testenv.Command(t, os.Args[0], "-test.run=^"+t.Name()+"$", "-test.timeout="+timeout.String(), "-test.parallel=4")
   775  		cmd.Env = append(cmd.Environ(), "GO_WANT_HELPER_PROCESS=1")
   776  		out, err := cmd.CombinedOutput()
   777  		t.Logf("%v:\n%s", cmd, out)
   778  		if _, ok := err.(*exec.ExitError); !ok {
   779  			t.Fatal(err)
   780  		}
   781  
   782  		// Because the outer subtests (and TestRunningTests itself) are marked as
   783  		// parallel, their test functions return (and are no longer “running”)
   784  		// before the inner subtests are released to run and hang.
   785  		// Only those inner subtests should be reported as running.
   786  		want := []string{
   787  			"TestRunningTests/outer0/inner0",
   788  			"TestRunningTests/outer0/inner1",
   789  			"TestRunningTests/outer1/inner0",
   790  			"TestRunningTests/outer1/inner1",
   791  		}
   792  
   793  		got, ok := parseRunningTests(out)
   794  		if slices.Equal(got, want) {
   795  			break
   796  		}
   797  		if ok {
   798  			t.Logf("found running tests:\n%s\nwant:\n%s", strings.Join(got, "\n"), strings.Join(want, "\n"))
   799  		} else {
   800  			t.Logf("no running tests found")
   801  		}
   802  		t.Logf("retrying with longer timeout")
   803  		timeout *= 2
   804  	}
   805  }
   806  
   807  func TestRunningTestsInCleanup(t *testing.T) {
   808  	t.Parallel()
   809  
   810  	if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
   811  		for i := 0; i < 2; i++ {
   812  			t.Run(fmt.Sprintf("outer%d", i), func(t *testing.T) {
   813  				// Not parallel: we expect to see only one outer test,
   814  				// stuck in cleanup after its subtest finishes.
   815  
   816  				t.Cleanup(func() {
   817  					for {
   818  						time.Sleep(1 * time.Millisecond)
   819  					}
   820  				})
   821  
   822  				for j := 0; j < 2; j++ {
   823  					t.Run(fmt.Sprintf("inner%d", j), func(t *testing.T) {
   824  						t.Parallel()
   825  					})
   826  				}
   827  			})
   828  		}
   829  	}
   830  
   831  	timeout := 10 * time.Millisecond
   832  	for {
   833  		cmd := testenv.Command(t, os.Args[0], "-test.run=^"+t.Name()+"$", "-test.timeout="+timeout.String())
   834  		cmd.Env = append(cmd.Environ(), "GO_WANT_HELPER_PROCESS=1")
   835  		out, err := cmd.CombinedOutput()
   836  		t.Logf("%v:\n%s", cmd, out)
   837  		if _, ok := err.(*exec.ExitError); !ok {
   838  			t.Fatal(err)
   839  		}
   840  
   841  		// TestRunningTestsInCleanup is blocked in the call to t.Run,
   842  		// but its test function has not yet returned so it should still
   843  		// be considered to be running.
   844  		// outer1 hasn't even started yet, so only outer0 and the top-level
   845  		// test function should be reported as running.
   846  		want := []string{
   847  			"TestRunningTestsInCleanup",
   848  			"TestRunningTestsInCleanup/outer0",
   849  		}
   850  
   851  		got, ok := parseRunningTests(out)
   852  		if slices.Equal(got, want) {
   853  			break
   854  		}
   855  		if ok {
   856  			t.Logf("found running tests:\n%s\nwant:\n%s", strings.Join(got, "\n"), strings.Join(want, "\n"))
   857  		} else {
   858  			t.Logf("no running tests found")
   859  		}
   860  		t.Logf("retrying with longer timeout")
   861  		timeout *= 2
   862  	}
   863  }
   864  
   865  func parseRunningTests(out []byte) (runningTests []string, ok bool) {
   866  	inRunningTests := false
   867  	for _, line := range strings.Split(string(out), "\n") {
   868  		if inRunningTests {
   869  			// Package testing adds one tab, the panic printer adds another.
   870  			if trimmed, ok := strings.CutPrefix(line, "\t\t"); ok {
   871  				if name, _, ok := strings.Cut(trimmed, " "); ok {
   872  					runningTests = append(runningTests, name)
   873  					continue
   874  				}
   875  			}
   876  
   877  			// This line is not the name of a running test.
   878  			return runningTests, true
   879  		}
   880  
   881  		if strings.TrimSpace(line) == "running tests:" {
   882  			inRunningTests = true
   883  		}
   884  	}
   885  
   886  	return nil, false
   887  }
   888  
   889  func TestConcurrentRun(t *testing.T) {
   890  	// Regression test for https://go.dev/issue/64402:
   891  	// this deadlocked after https://go.dev/cl/506755.
   892  
   893  	block := make(chan struct{})
   894  	var ready, done sync.WaitGroup
   895  	for i := 0; i < 2; i++ {
   896  		ready.Add(1)
   897  		done.Add(1)
   898  		go t.Run("", func(*testing.T) {
   899  			ready.Done()
   900  			<-block
   901  			done.Done()
   902  		})
   903  	}
   904  	ready.Wait()
   905  	close(block)
   906  	done.Wait()
   907  }
   908  
   909  func TestParentRun(t1 *testing.T) {
   910  	// Regression test for https://go.dev/issue/64402:
   911  	// this deadlocked after https://go.dev/cl/506755.
   912  
   913  	t1.Run("outer", func(t2 *testing.T) {
   914  		t2.Log("Hello outer!")
   915  		t1.Run("not_inner", func(t3 *testing.T) { // Note: this is t1.Run, not t2.Run.
   916  			t3.Log("Hello inner!")
   917  		})
   918  	})
   919  }
   920  
   921  func TestContext(t *testing.T) {
   922  	ctx := t.Context()
   923  	if err := ctx.Err(); err != nil {
   924  		t.Fatalf("expected non-canceled context, got %v", err)
   925  	}
   926  
   927  	var innerCtx context.Context
   928  	t.Run("inner", func(t *testing.T) {
   929  		innerCtx = t.Context()
   930  		if err := innerCtx.Err(); err != nil {
   931  			t.Fatalf("expected inner test to not inherit canceled context, got %v", err)
   932  		}
   933  	})
   934  	t.Run("inner2", func(t *testing.T) {
   935  		if !errors.Is(innerCtx.Err(), context.Canceled) {
   936  			t.Fatal("expected context of sibling test to be canceled after its test function finished")
   937  		}
   938  	})
   939  
   940  	t.Cleanup(func() {
   941  		if !errors.Is(ctx.Err(), context.Canceled) {
   942  			t.Fatal("expected context canceled before cleanup")
   943  		}
   944  	})
   945  }
   946  

View as plain text