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