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 TestBenchmarkRaceBLoop(t *testing.T) {
   704  	out := runTest(t, "BenchmarkBLoopRacy")
   705  	c := bytes.Count(out, []byte("race detected during execution of test"))
   706  
   707  	want := 0
   708  	// We should see one race detector report.
   709  	if race.Enabled {
   710  		want = 1
   711  	}
   712  	if c != want {
   713  		t.Errorf("got %d race reports; want %d", c, want)
   714  	}
   715  }
   716  
   717  func BenchmarkRacy(b *testing.B) {
   718  	if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
   719  		b.Skipf("skipping intentionally-racy benchmark")
   720  	}
   721  	for i := 0; i < b.N; i++ {
   722  		doRace()
   723  	}
   724  }
   725  
   726  func BenchmarkBLoopRacy(b *testing.B) {
   727  	if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
   728  		b.Skipf("skipping intentionally-racy benchmark")
   729  	}
   730  	for b.Loop() {
   731  		doRace()
   732  	}
   733  }
   734  
   735  func TestBenchmarkSubRace(t *testing.T) {
   736  	out := runTest(t, "BenchmarkSubRacy")
   737  	c := bytes.Count(out, []byte("race detected during execution of test"))
   738  
   739  	want := 0
   740  	// We should see 3 race detector reports:
   741  	// one in the sub-bencmark, one in the parent afterward,
   742  	// and one in b.Loop.
   743  	if race.Enabled {
   744  		want = 3
   745  	}
   746  	if c != want {
   747  		t.Errorf("got %d race reports; want %d", c, want)
   748  	}
   749  }
   750  
   751  func BenchmarkSubRacy(b *testing.B) {
   752  	if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
   753  		b.Skipf("skipping intentionally-racy benchmark")
   754  	}
   755  
   756  	b.Run("non-racy", func(b *testing.B) {
   757  		tot := 0
   758  		for i := 0; i < b.N; i++ {
   759  			tot++
   760  		}
   761  		_ = tot
   762  	})
   763  
   764  	b.Run("racy", func(b *testing.B) {
   765  		for i := 0; i < b.N; i++ {
   766  			doRace()
   767  		}
   768  	})
   769  
   770  	b.Run("racy-bLoop", func(b *testing.B) {
   771  		for b.Loop() {
   772  			doRace()
   773  		}
   774  	})
   775  
   776  	doRace() // should be reported separately
   777  }
   778  
   779  func TestRunningTests(t *testing.T) {
   780  	t.Parallel()
   781  
   782  	// Regression test for https://go.dev/issue/64404:
   783  	// on timeout, the "running tests" message should not include
   784  	// tests that are waiting on parked subtests.
   785  
   786  	if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
   787  		for i := 0; i < 2; i++ {
   788  			t.Run(fmt.Sprintf("outer%d", i), func(t *testing.T) {
   789  				t.Parallel()
   790  				for j := 0; j < 2; j++ {
   791  					t.Run(fmt.Sprintf("inner%d", j), func(t *testing.T) {
   792  						t.Parallel()
   793  						for {
   794  							time.Sleep(1 * time.Millisecond)
   795  						}
   796  					})
   797  				}
   798  			})
   799  		}
   800  	}
   801  
   802  	timeout := 10 * time.Millisecond
   803  	for {
   804  		cmd := testenv.Command(t, os.Args[0], "-test.run=^"+t.Name()+"$", "-test.timeout="+timeout.String(), "-test.parallel=4")
   805  		cmd.Env = append(cmd.Environ(), "GO_WANT_HELPER_PROCESS=1")
   806  		out, err := cmd.CombinedOutput()
   807  		t.Logf("%v:\n%s", cmd, out)
   808  		if _, ok := err.(*exec.ExitError); !ok {
   809  			t.Fatal(err)
   810  		}
   811  
   812  		// Because the outer subtests (and TestRunningTests itself) are marked as
   813  		// parallel, their test functions return (and are no longer “running”)
   814  		// before the inner subtests are released to run and hang.
   815  		// Only those inner subtests should be reported as running.
   816  		want := []string{
   817  			"TestRunningTests/outer0/inner0",
   818  			"TestRunningTests/outer0/inner1",
   819  			"TestRunningTests/outer1/inner0",
   820  			"TestRunningTests/outer1/inner1",
   821  		}
   822  
   823  		got, ok := parseRunningTests(out)
   824  		if slices.Equal(got, want) {
   825  			break
   826  		}
   827  		if ok {
   828  			t.Logf("found running tests:\n%s\nwant:\n%s", strings.Join(got, "\n"), strings.Join(want, "\n"))
   829  		} else {
   830  			t.Logf("no running tests found")
   831  		}
   832  		t.Logf("retrying with longer timeout")
   833  		timeout *= 2
   834  	}
   835  }
   836  
   837  func TestRunningTestsInCleanup(t *testing.T) {
   838  	t.Parallel()
   839  
   840  	if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
   841  		for i := 0; i < 2; i++ {
   842  			t.Run(fmt.Sprintf("outer%d", i), func(t *testing.T) {
   843  				// Not parallel: we expect to see only one outer test,
   844  				// stuck in cleanup after its subtest finishes.
   845  
   846  				t.Cleanup(func() {
   847  					for {
   848  						time.Sleep(1 * time.Millisecond)
   849  					}
   850  				})
   851  
   852  				for j := 0; j < 2; j++ {
   853  					t.Run(fmt.Sprintf("inner%d", j), func(t *testing.T) {
   854  						t.Parallel()
   855  					})
   856  				}
   857  			})
   858  		}
   859  	}
   860  
   861  	timeout := 10 * time.Millisecond
   862  	for {
   863  		cmd := testenv.Command(t, os.Args[0], "-test.run=^"+t.Name()+"$", "-test.timeout="+timeout.String())
   864  		cmd.Env = append(cmd.Environ(), "GO_WANT_HELPER_PROCESS=1")
   865  		out, err := cmd.CombinedOutput()
   866  		t.Logf("%v:\n%s", cmd, out)
   867  		if _, ok := err.(*exec.ExitError); !ok {
   868  			t.Fatal(err)
   869  		}
   870  
   871  		// TestRunningTestsInCleanup is blocked in the call to t.Run,
   872  		// but its test function has not yet returned so it should still
   873  		// be considered to be running.
   874  		// outer1 hasn't even started yet, so only outer0 and the top-level
   875  		// test function should be reported as running.
   876  		want := []string{
   877  			"TestRunningTestsInCleanup",
   878  			"TestRunningTestsInCleanup/outer0",
   879  		}
   880  
   881  		got, ok := parseRunningTests(out)
   882  		if slices.Equal(got, want) {
   883  			break
   884  		}
   885  		if ok {
   886  			t.Logf("found running tests:\n%s\nwant:\n%s", strings.Join(got, "\n"), strings.Join(want, "\n"))
   887  		} else {
   888  			t.Logf("no running tests found")
   889  		}
   890  		t.Logf("retrying with longer timeout")
   891  		timeout *= 2
   892  	}
   893  }
   894  
   895  func parseRunningTests(out []byte) (runningTests []string, ok bool) {
   896  	inRunningTests := false
   897  	for _, line := range strings.Split(string(out), "\n") {
   898  		if inRunningTests {
   899  			// Package testing adds one tab, the panic printer adds another.
   900  			if trimmed, ok := strings.CutPrefix(line, "\t\t"); ok {
   901  				if name, _, ok := strings.Cut(trimmed, " "); ok {
   902  					runningTests = append(runningTests, name)
   903  					continue
   904  				}
   905  			}
   906  
   907  			// This line is not the name of a running test.
   908  			return runningTests, true
   909  		}
   910  
   911  		if strings.TrimSpace(line) == "running tests:" {
   912  			inRunningTests = true
   913  		}
   914  	}
   915  
   916  	return nil, false
   917  }
   918  
   919  func TestConcurrentRun(t *testing.T) {
   920  	// Regression test for https://go.dev/issue/64402:
   921  	// this deadlocked after https://go.dev/cl/506755.
   922  
   923  	block := make(chan struct{})
   924  	var ready, done sync.WaitGroup
   925  	for i := 0; i < 2; i++ {
   926  		ready.Add(1)
   927  		done.Add(1)
   928  		go t.Run("", func(*testing.T) {
   929  			ready.Done()
   930  			<-block
   931  			done.Done()
   932  		})
   933  	}
   934  	ready.Wait()
   935  	close(block)
   936  	done.Wait()
   937  }
   938  
   939  func TestParentRun(t1 *testing.T) {
   940  	// Regression test for https://go.dev/issue/64402:
   941  	// this deadlocked after https://go.dev/cl/506755.
   942  
   943  	t1.Run("outer", func(t2 *testing.T) {
   944  		t2.Log("Hello outer!")
   945  		t1.Run("not_inner", func(t3 *testing.T) { // Note: this is t1.Run, not t2.Run.
   946  			t3.Log("Hello inner!")
   947  		})
   948  	})
   949  }
   950  
   951  func TestContext(t *testing.T) {
   952  	ctx := t.Context()
   953  	if err := ctx.Err(); err != nil {
   954  		t.Fatalf("expected non-canceled context, got %v", err)
   955  	}
   956  
   957  	var innerCtx context.Context
   958  	t.Run("inner", func(t *testing.T) {
   959  		innerCtx = t.Context()
   960  		if err := innerCtx.Err(); err != nil {
   961  			t.Fatalf("expected inner test to not inherit canceled context, got %v", err)
   962  		}
   963  	})
   964  	t.Run("inner2", func(t *testing.T) {
   965  		if !errors.Is(innerCtx.Err(), context.Canceled) {
   966  			t.Fatal("expected context of sibling test to be canceled after its test function finished")
   967  		}
   968  	})
   969  
   970  	t.Cleanup(func() {
   971  		if !errors.Is(ctx.Err(), context.Canceled) {
   972  			t.Fatal("expected context canceled before cleanup")
   973  		}
   974  	})
   975  }
   976  
   977  func TestBenchmarkBLoopIterationCorrect(t *testing.T) {
   978  	out := runTest(t, "BenchmarkBLoopPrint")
   979  	c := bytes.Count(out, []byte("Printing from BenchmarkBLoopPrint"))
   980  
   981  	want := 2
   982  	if c != want {
   983  		t.Errorf("got %d loop iterations; want %d", c, want)
   984  	}
   985  
   986  	// b.Loop() will only rampup once.
   987  	c = bytes.Count(out, []byte("Ramping up from BenchmarkBLoopPrint"))
   988  	want = 1
   989  	if c != want {
   990  		t.Errorf("got %d loop rampup; want %d", c, want)
   991  	}
   992  
   993  	re := regexp.MustCompile(`BenchmarkBLoopPrint(-[0-9]+)?\s+2\s+[0-9]+\s+ns/op`)
   994  	if !re.Match(out) {
   995  		t.Error("missing benchmark output")
   996  	}
   997  }
   998  
   999  func TestBenchmarkBNIterationCorrect(t *testing.T) {
  1000  	out := runTest(t, "BenchmarkBNPrint")
  1001  	c := bytes.Count(out, []byte("Printing from BenchmarkBNPrint"))
  1002  
  1003  	// runTest sets benchtime=2x, with semantics specified in #32051 it should
  1004  	// run 3 times.
  1005  	want := 3
  1006  	if c != want {
  1007  		t.Errorf("got %d loop iterations; want %d", c, want)
  1008  	}
  1009  
  1010  	// b.N style fixed iteration loop will rampup twice:
  1011  	// One in run1(), the other in launch
  1012  	c = bytes.Count(out, []byte("Ramping up from BenchmarkBNPrint"))
  1013  	want = 2
  1014  	if c != want {
  1015  		t.Errorf("got %d loop rampup; want %d", c, want)
  1016  	}
  1017  }
  1018  
  1019  func BenchmarkBLoopPrint(b *testing.B) {
  1020  	b.Logf("Ramping up from BenchmarkBLoopPrint")
  1021  	for b.Loop() {
  1022  		b.Logf("Printing from BenchmarkBLoopPrint")
  1023  	}
  1024  }
  1025  
  1026  func BenchmarkBNPrint(b *testing.B) {
  1027  	b.Logf("Ramping up from BenchmarkBNPrint")
  1028  	for i := 0; i < b.N; i++ {
  1029  		b.Logf("Printing from BenchmarkBNPrint")
  1030  	}
  1031  }
  1032  

View as plain text