]> Cypherpunks.ru repositories - gostls13.git/blob - src/math/rand/v2/default_test.go
math/rand/v2: clean up regression test
[gostls13.git] / src / math / rand / v2 / default_test.go
1 // Copyright 2023 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 rand_test
6
7 import (
8         "fmt"
9         "internal/race"
10         "internal/testenv"
11         . "math/rand/v2"
12         "os"
13         "runtime"
14         "strconv"
15         "sync"
16         "testing"
17 )
18
19 // Test that racy access to the default functions behaves reasonably.
20 func TestDefaultRace(t *testing.T) {
21         // Skip the test in short mode, but even in short mode run
22         // the test if we are using the race detector, because part
23         // of this is to see whether the race detector reports any problems.
24         if testing.Short() && !race.Enabled {
25                 t.Skip("skipping starting another executable in short mode")
26         }
27
28         const env = "GO_RAND_TEST_HELPER_CODE"
29         if v := os.Getenv(env); v != "" {
30                 doDefaultTest(t, v)
31                 return
32         }
33
34         t.Parallel()
35
36         for i := 0; i < 6; i++ {
37                 i := i
38                 t.Run(strconv.Itoa(i), func(t *testing.T) {
39                         t.Parallel()
40                         exe, err := os.Executable()
41                         if err != nil {
42                                 exe = os.Args[0]
43                         }
44                         cmd := testenv.Command(t, exe, "-test.run=TestDefaultRace")
45                         cmd = testenv.CleanCmdEnv(cmd)
46                         cmd.Env = append(cmd.Env, fmt.Sprintf("GO_RAND_TEST_HELPER_CODE=%d", i/2))
47                         if i%2 != 0 {
48                                 cmd.Env = append(cmd.Env, "GODEBUG=randautoseed=0")
49                         }
50                         out, err := cmd.CombinedOutput()
51                         if len(out) > 0 {
52                                 t.Logf("%s", out)
53                         }
54                         if err != nil {
55                                 t.Error(err)
56                         }
57                 })
58         }
59 }
60
61 // doDefaultTest should be run before there have been any calls to the
62 // top-level math/rand functions. Make sure that we can make concurrent
63 // calls to top-level functions and to Seed without any duplicate values.
64 // This will also give the race detector a change to report any problems.
65 func doDefaultTest(t *testing.T, v string) {
66         code, err := strconv.Atoi(v)
67         if err != nil {
68                 t.Fatalf("internal error: unrecognized code %q", v)
69         }
70
71         goroutines := runtime.GOMAXPROCS(0)
72         if goroutines < 4 {
73                 goroutines = 4
74         }
75
76         ch := make(chan uint64, goroutines*3)
77         var wg sync.WaitGroup
78
79         // The various tests below should not cause race detector reports
80         // and should not produce duplicate results.
81         //
82         // Note: these tests can theoretically fail when using fastrand64
83         // in that it is possible to coincidentally get the same random
84         // number twice. That could happen something like 1 / 2**64 times,
85         // which is rare enough that it may never happen. We don't worry
86         // about that case.
87
88         switch code {
89         case 0:
90                 // Call Seed and Uint64 concurrently.
91                 wg.Add(goroutines)
92                 for i := 0; i < goroutines; i++ {
93                         go func(s int64) {
94                                 defer wg.Done()
95                                 Seed(s)
96                         }(int64(i) + 100)
97                 }
98                 wg.Add(goroutines)
99                 for i := 0; i < goroutines; i++ {
100                         go func() {
101                                 defer wg.Done()
102                                 ch <- Uint64()
103                         }()
104                 }
105         case 1:
106                 // Call Uint64 concurrently with no Seed.
107                 wg.Add(goroutines)
108                 for i := 0; i < goroutines; i++ {
109                         go func() {
110                                 defer wg.Done()
111                                 ch <- Uint64()
112                         }()
113                 }
114         case 2:
115                 // Start with Uint64 to pick the fast source, then call
116                 // Seed and Uint64 concurrently.
117                 ch <- Uint64()
118                 wg.Add(goroutines)
119                 for i := 0; i < goroutines; i++ {
120                         go func(s int64) {
121                                 defer wg.Done()
122                                 Seed(s)
123                         }(int64(i) + 100)
124                 }
125                 wg.Add(goroutines)
126                 for i := 0; i < goroutines; i++ {
127                         go func() {
128                                 defer wg.Done()
129                                 ch <- Uint64()
130                         }()
131                 }
132         default:
133                 t.Fatalf("internal error: unrecognized code %d", code)
134         }
135
136         go func() {
137                 wg.Wait()
138                 close(ch)
139         }()
140
141         m := make(map[uint64]bool)
142         for i := range ch {
143                 if m[i] {
144                         t.Errorf("saw %d twice", i)
145                 }
146                 m[i] = true
147         }
148 }