Source file src/math/rand/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"
    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  			cmd := testenv.Command(t, testenv.Executable(t), "-test.run=TestDefaultRace")
    41  			cmd = testenv.CleanCmdEnv(cmd)
    42  			cmd.Env = append(cmd.Env, fmt.Sprintf("GO_RAND_TEST_HELPER_CODE=%d", i/2))
    43  			if i%2 != 0 {
    44  				cmd.Env = append(cmd.Env, "GODEBUG=randautoseed=0")
    45  			}
    46  			out, err := cmd.CombinedOutput()
    47  			if len(out) > 0 {
    48  				t.Logf("%s", out)
    49  			}
    50  			if err != nil {
    51  				t.Error(err)
    52  			}
    53  		})
    54  	}
    55  }
    56  
    57  // doDefaultTest should be run before there have been any calls to the
    58  // top-level math/rand functions. Make sure that we can make concurrent
    59  // calls to top-level functions and to Seed without any duplicate values.
    60  // This will also give the race detector a change to report any problems.
    61  func doDefaultTest(t *testing.T, v string) {
    62  	code, err := strconv.Atoi(v)
    63  	if err != nil {
    64  		t.Fatalf("internal error: unrecognized code %q", v)
    65  	}
    66  
    67  	goroutines := runtime.GOMAXPROCS(0)
    68  	if goroutines < 4 {
    69  		goroutines = 4
    70  	}
    71  
    72  	ch := make(chan uint64, goroutines*3)
    73  	var wg sync.WaitGroup
    74  
    75  	// The various tests below should not cause race detector reports
    76  	// and should not produce duplicate results.
    77  	//
    78  	// Note: these tests can theoretically fail when using fastrand64
    79  	// in that it is possible to coincidentally get the same random
    80  	// number twice. That could happen something like 1 / 2**64 times,
    81  	// which is rare enough that it may never happen. We don't worry
    82  	// about that case.
    83  
    84  	switch code {
    85  	case 0:
    86  		// Call Seed and Uint64 concurrently.
    87  		wg.Add(goroutines)
    88  		for i := 0; i < goroutines; i++ {
    89  			go func(s int64) {
    90  				defer wg.Done()
    91  				Seed(s)
    92  			}(int64(i) + 100)
    93  		}
    94  		wg.Add(goroutines)
    95  		for i := 0; i < goroutines; i++ {
    96  			go func() {
    97  				defer wg.Done()
    98  				ch <- Uint64()
    99  			}()
   100  		}
   101  	case 1:
   102  		// Call Uint64 concurrently with no Seed.
   103  		wg.Add(goroutines)
   104  		for i := 0; i < goroutines; i++ {
   105  			go func() {
   106  				defer wg.Done()
   107  				ch <- Uint64()
   108  			}()
   109  		}
   110  	case 2:
   111  		// Start with Uint64 to pick the fast source, then call
   112  		// Seed and Uint64 concurrently.
   113  		ch <- Uint64()
   114  		wg.Add(goroutines)
   115  		for i := 0; i < goroutines; i++ {
   116  			go func(s int64) {
   117  				defer wg.Done()
   118  				Seed(s)
   119  			}(int64(i) + 100)
   120  		}
   121  		wg.Add(goroutines)
   122  		for i := 0; i < goroutines; i++ {
   123  			go func() {
   124  				defer wg.Done()
   125  				ch <- Uint64()
   126  			}()
   127  		}
   128  	default:
   129  		t.Fatalf("internal error: unrecognized code %d", code)
   130  	}
   131  
   132  	go func() {
   133  		wg.Wait()
   134  		close(ch)
   135  	}()
   136  
   137  	m := make(map[uint64]bool)
   138  	for i := range ch {
   139  		if m[i] {
   140  			t.Errorf("saw %d twice", i)
   141  		}
   142  		m[i] = true
   143  	}
   144  }
   145  

View as plain text