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

View as plain text