61 lines
1.5 KiB
Go
61 lines
1.5 KiB
Go
// Package mtest contains types and functions which are useful when writing
|
|
// tests
|
|
package mtest
|
|
|
|
import (
|
|
crand "crypto/rand"
|
|
"encoding/hex"
|
|
"math/rand"
|
|
"reflect"
|
|
"time"
|
|
)
|
|
|
|
// Rand is a public instance of rand.Rand, seeded with the current
|
|
// nano-timestamp
|
|
var Rand = rand.New(rand.NewSource(time.Now().UnixNano()))
|
|
|
|
// RandBytes returns n random bytes
|
|
func RandBytes(n int) []byte {
|
|
b := make([]byte, n)
|
|
if _, err := crand.Read(b); err != nil {
|
|
panic(err)
|
|
}
|
|
return b
|
|
}
|
|
|
|
// RandHex returns a random hex string which is n characters long
|
|
func RandHex(n int) string {
|
|
b := RandBytes(hex.DecodedLen(n))
|
|
return hex.EncodeToString(b)
|
|
}
|
|
|
|
// RandElement returns a random element from the given slice.
|
|
//
|
|
// If a weighting function is given then that function is used to weight each
|
|
// element of the slice relative to the others, based on whatever metric and
|
|
// scale is desired. The weight function must be able to be called more than
|
|
// once on each element.
|
|
func RandElement(slice interface{}, weight func(i int) uint64) interface{} {
|
|
v := reflect.ValueOf(slice)
|
|
l := v.Len()
|
|
|
|
if weight == nil {
|
|
return v.Index(Rand.Intn(l)).Interface()
|
|
}
|
|
|
|
var totalWeight uint64
|
|
for i := 0; i < l; i++ {
|
|
totalWeight += weight(i)
|
|
}
|
|
|
|
target := Rand.Int63n(int64(totalWeight))
|
|
for i := 0; i < l; i++ {
|
|
w := int64(weight(i))
|
|
target -= w
|
|
if target < 0 {
|
|
return v.Index(i).Interface()
|
|
}
|
|
}
|
|
panic("should never get here, perhaps the weighting function is inconsistent?")
|
|
}
|