Skip to content

Commit

Permalink
reused version4 NewRandom() and added better testing
Browse files Browse the repository at this point in the history
  • Loading branch information
trabetti committed Feb 28, 2019
1 parent caad4a1 commit 63b66dd
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 15 deletions.
30 changes: 22 additions & 8 deletions uuid_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,31 @@ import (
"crypto/rand"
)

// A UuidSource holds a random number generator and generates UUIDs using it as its source.
//
// It is useful when a process need its own random number generator,
// e.g. when running some processes concurrently.
type UuidSource struct {
rander io.Reader
}

// Creates a new UuidSource which holds its own random number generator.
//
// Calling NewSource with nil sets the random number generator to a default
// generator.
func NewSource(r io.Reader) UuidSource {
var uuidSource UuidSource
uuidSource.SetRand(r)
return uuidSource

}

// SetRand sets the random number generator of the UuidSource to r, which implements io.Reader.
// If r.Read returns an error when the package requests random data then
// a panic will be issued.
//
// Calling SetRand with nil sets the random number generator to a default
// generator.
func (uuidSource *UuidSource) SetRand(r io.Reader) {
if r == nil {
uuidSource.rander = rand.Reader
Expand All @@ -24,17 +38,17 @@ func (uuidSource *UuidSource) SetRand(r io.Reader) {
uuidSource.rander = r
}

// NewRandom returns a Random (Version 4) UUID based on the random number generator in the UuidSource.
//
// See more detailed explanation here: https://godoc.org/github.com/google/uuid#NewRandom
func (uuidSource UuidSource) NewRandom() (UUID, error) {
var uuid UUID
_, err := io.ReadFull(uuidSource.rander, uuid[:])
if err != nil {
return Nil, err
}
uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4
uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10
return uuid, nil
return newRandom(uuidSource.rander)
}

// New creates a new random UUID based on the random number generator in the UuidSource or panics. New is equivalent to
// the expression
//
// uuid.Must(uuid.NewRandom())
func (uuidSource UuidSource) New() UUID {
return Must(uuidSource.NewRandom())
}
42 changes: 36 additions & 6 deletions uuid_source_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,53 @@ import (
)

func TestUuidSources(t *testing.T) {

// Two identical sources, should give same sequence
currentTime := time.Now().UnixNano()
uuidSourceA := NewSource(rand.New(rand.NewSource(currentTime)))
uuidSourceB := NewSource(rand.New(rand.NewSource(currentTime)))

for i := 0; i < 10; i++ {
if uuidSourceA.New().String() != uuidSourceB.New().String() {
t.Error("Uuid values are not reproducaible!")
uuid1 := uuidSourceA.New()
uuid2 := uuidSourceB.New()
if uuid1 != uuid2 {
t.Errorf("expected duplicates, got %q and %q", uuid1, uuid2)
}
}

uuidSourceA = NewSource(rand.New(rand.NewSource(123)))
uuidSourceB = NewSource(rand.New(rand.NewSource(456)))
// Set rander with nil, each source will be random
uuidSourceA.SetRand(nil)
uuidSourceB.SetRand(nil)

for i := 0; i < 10; i++ {
uuid1 := uuidSourceA.New()
uuid2 := uuidSourceB.New()
if uuid1 == uuid2 {
t.Errorf("unexpected duplicates, got %q", uuid1)
}
}

// Set rander to rand source with same seed, should give same sequence
uuidSourceA.SetRand(rand.New(rand.NewSource(123)))
uuidSourceB.SetRand(rand.New(rand.NewSource(123)))

for i := 0; i < 10; i++ {
uuid1 := uuidSourceA.New()
uuid2 := uuidSourceB.New()
if uuid1 != uuid2 {
t.Errorf("expected duplicates, got %q and %q", uuid1, uuid2)
}
}

// Set rander to rand source with different seeds, should not give same sequence
uuidSourceA.SetRand(rand.New(rand.NewSource(456)))
uuidSourceB.SetRand(rand.New(rand.NewSource(789)))

for i := 0; i < 10; i++ {
if uuidSourceA.New().String() == uuidSourceB.New().String() {
t.Error("Uuid values should not match!")
uuid1 := uuidSourceA.New()
uuid2 := uuidSourceB.New()
if uuid1 == uuid2 {
t.Errorf("unexpected duplicates, got %q", uuid1)
}
}

Expand Down
21 changes: 21 additions & 0 deletions uuid_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import (
"testing"
"time"
"unsafe"

"math/rand"
)

type test struct {
Expand Down Expand Up @@ -479,6 +481,25 @@ func TestBadRand(t *testing.T) {
}
}

func TestSetRand(t *testing.T) {
SetRand(rand.New(rand.NewSource(456)))
uuid1 := New()
uuid2 := New()

SetRand(rand.New(rand.NewSource(456)))
uuid3 := New()
uuid4 := New()

if uuid1 != uuid3 {
t.Errorf("expected duplicates, got %q and %q", uuid1, uuid3)
}
if uuid2 != uuid4 {
t.Errorf("expected duplicates, got %q and %q", uuid2, uuid4)
}


}

var asString = "f47ac10b-58cc-0372-8567-0e02b2c3d479"
var asBytes = []byte(asString)

Expand Down
7 changes: 6 additions & 1 deletion version4.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,17 @@ func New() UUID {
// equivalent to the odds of creating a few tens of trillions of UUIDs in a
// year and having one duplicate.
func NewRandom() (UUID, error) {
return newRandom(rander)
}

func newRandom(r io.Reader) (UUID, error) {
var uuid UUID
_, err := io.ReadFull(rander, uuid[:])
_, err := io.ReadFull(r, uuid[:])
if err != nil {
return Nil, err
}
uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4
uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10
return uuid, nil
}

0 comments on commit 63b66dd

Please sign in to comment.