tendermint/common/random_test.go

130 lines
3.2 KiB
Go
Raw Normal View History

package common_test
import (
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"sync"
"testing"
"time"
2017-12-14 21:23:25 -08:00
"github.com/stretchr/testify/assert"
"github.com/tendermint/tmlibs/common"
)
2017-12-14 21:23:25 -08:00
func TestRandStr(t *testing.T) {
l := 243
s := common.RandStr(l)
assert.Equal(t, l, len(s))
}
func TestRandBytes(t *testing.T) {
l := 243
b := common.RandBytes(l)
assert.Equal(t, l, len(b))
}
func TestRandIntn(t *testing.T) {
n := 243
for i := 0; i < 100; i++ {
x := common.RandIntn(n)
assert.True(t, x < n)
}
}
// It is essential that these tests run and never repeat their outputs
// lest we've been pwned and the behavior of our randomness is controlled.
// See Issues:
// * https://github.com/tendermint/tmlibs/issues/99
// * https://github.com/tendermint/tendermint/issues/973
func TestUniqueRng(t *testing.T) {
if os.Getenv("TENDERMINT_INTEGRATION_TESTS") == "" {
t.Skipf("Can only be run as an integration test")
}
// The goal of this test is to invoke the
// Rand* tests externally with no repeating results, booted up.
// Any repeated results indicate that the seed is the same or that
// perhaps we are using math/rand directly.
tmpDir, err := ioutil.TempDir("", "rng-tests")
if err != nil {
t.Fatalf("Creating tempDir: %v", err)
}
defer os.RemoveAll(tmpDir)
outpath := filepath.Join(tmpDir, "main.go")
f, err := os.Create(outpath)
if err != nil {
t.Fatalf("Setting up %q err: %v", outpath, err)
}
f.Write([]byte(integrationTestProgram))
if err := f.Close(); err != nil {
t.Fatalf("Closing: %v", err)
}
outputs := make(map[string][]int)
for i := 0; i < 100; i++ {
cmd := exec.Command("go", "run", outpath)
bOutput, err := cmd.CombinedOutput()
if err != nil {
t.Errorf("Run #%d: err: %v output: %s", i, err, bOutput)
continue
}
output := string(bOutput)
runs, seen := outputs[output]
if seen {
t.Errorf("Run #%d's output was already seen in previous runs: %v", i, runs)
}
outputs[output] = append(outputs[output], i)
}
}
const integrationTestProgram = `
package main
import (
"encoding/json"
"fmt"
"math/rand"
"github.com/tendermint/tmlibs/common"
)
func main() {
// Set math/rand's Seed so that any direct invocations
// of math/rand will reveal themselves.
rand.Seed(1)
perm := common.RandPerm(10)
blob, _ := json.Marshal(perm)
fmt.Printf("perm: %s\n", blob)
fmt.Printf("randInt: %d\n", common.RandInt())
fmt.Printf("randUint: %d\n", common.RandUint())
fmt.Printf("randIntn: %d\n", common.RandIntn(97))
fmt.Printf("randInt31: %d\n", common.RandInt31())
fmt.Printf("randInt32: %d\n", common.RandInt32())
fmt.Printf("randInt63: %d\n", common.RandInt63())
fmt.Printf("randInt64: %d\n", common.RandInt64())
fmt.Printf("randUint32: %d\n", common.RandUint32())
fmt.Printf("randUint64: %d\n", common.RandUint64())
fmt.Printf("randUint16Exp: %d\n", common.RandUint16Exp())
fmt.Printf("randUint32Exp: %d\n", common.RandUint32Exp())
fmt.Printf("randUint64Exp: %d\n", common.RandUint64Exp())
}`
func TestRngConcurrencySafety(t *testing.T) {
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
wg.Add(1)
go func() {
defer wg.Done()
_ = common.RandUint64()
<-time.After(time.Millisecond * time.Duration(common.RandIntn(100)))
_ = common.RandPerm(3)
}()
}
wg.Wait()
}