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

View as plain text