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 5package testing_test 6 7import ( 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 27func 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 46func 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 65func 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 78func 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 91func 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 146func 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 203func 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 216func 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 229func 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 244func 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. 262var testingTrueInInit = false 263 264// testingTrueInPackageVarInit is part of TestTesting. 265var testingTrueInPackageVarInit = testing.Testing() 266 267// init is part of TestTesting. 268func init() { 269 if testing.Testing() { 270 testingTrueInInit = true 271 } 272} 273 274var testingProg = ` 275package main 276 277import ( 278 "fmt" 279 "testing" 280) 281 282func main() { 283 fmt.Println(testing.Testing()) 284} 285` 286 287func 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. 322func 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. 343func 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 354func 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. 377func 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 390func 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 437func 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 472func 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 498func 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 538func 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 560func 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 585func 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 599func 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 608func 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 623func 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 645func 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 703func 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 761func parseRunningTests(out []byte) (runningTests []string, ok bool) { 762 inRunningTests := false 763 for _, line := range strings.Split(string(out), "\n") { 764 if inRunningTests { 765 // Package testing adds one tab, the panic printer adds another. 766 if trimmed, ok := strings.CutPrefix(line, "\t\t"); ok { 767 if name, _, ok := strings.Cut(trimmed, " "); ok { 768 runningTests = append(runningTests, name) 769 continue 770 } 771 } 772 773 // This line is not the name of a running test. 774 return runningTests, true 775 } 776 777 if strings.TrimSpace(line) == "running tests:" { 778 inRunningTests = true 779 } 780 } 781 782 return nil, false 783} 784 785func TestConcurrentRun(t *testing.T) { 786 // Regression test for https://go.dev/issue/64402: 787 // this deadlocked after https://go.dev/cl/506755. 788 789 block := make(chan struct{}) 790 var ready, done sync.WaitGroup 791 for i := 0; i < 2; i++ { 792 ready.Add(1) 793 done.Add(1) 794 go t.Run("", func(*testing.T) { 795 ready.Done() 796 <-block 797 done.Done() 798 }) 799 } 800 ready.Wait() 801 close(block) 802 done.Wait() 803} 804 805func TestParentRun(t1 *testing.T) { 806 // Regression test for https://go.dev/issue/64402: 807 // this deadlocked after https://go.dev/cl/506755. 808 809 t1.Run("outer", func(t2 *testing.T) { 810 t2.Log("Hello outer!") 811 t1.Run("not_inner", func(t3 *testing.T) { // Note: this is t1.Run, not t2.Run. 812 t3.Log("Hello inner!") 813 }) 814 }) 815} 816