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  			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  }
   149  

View as plain text