Source file
src/testing/testing_test.go
Documentation: testing
1
2
3
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
24
25
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
35
36
37
38
39
40
41
42
43
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
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
95 select {
96 case dir := <-dirCh:
97 fi, err := os.Stat(dir)
98 if os.IsNotExist(err) {
99
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
262 var testingTrueInInit = false
263
264
265 var testingTrueInPackageVarInit = testing.Testing()
266
267
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
321
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
342
343 func doRace() {
344 var x int
345 c1 := make(chan bool)
346 go func() {
347 x = 1
348 c1 <- true
349 }()
350 _ = x
351 <-c1
352 }
353
354 func TestRaceReports(t *testing.T) {
355 if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
356
357 t.Run("Sub", func(t *testing.T) {
358 doRace()
359 })
360 return
361 }
362
363 out := runTest(t, "TestRaceReports")
364
365
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
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
413
414
415
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
426
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
444 })
445 return
446 }
447
448 out := runTest(t, "TestRaceInCleanup")
449
450
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
461
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
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()
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
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
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
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
614
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()
643 }
644
645 func TestRunningTests(t *testing.T) {
646 t.Parallel()
647
648
649
650
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
679
680
681
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
710
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
738
739
740
741
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
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
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
785 func TestConcurrentRun(t *testing.T) {
786
787
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
805 func TestParentRun(t1 *testing.T) {
806
807
808
809 t1.Run("outer", func(t2 *testing.T) {
810 t2.Log("Hello outer!")
811 t1.Run("not_inner", func(t3 *testing.T) {
812 t3.Log("Hello inner!")
813 })
814 })
815 }
816
View as plain text