From 63b66ddfe0ddd512d239ead433bc6124ae02a56b Mon Sep 17 00:00:00 2001 From: talis Date: Thu, 28 Feb 2019 14:59:24 +0200 Subject: [PATCH] reused version4 NewRandom() and added better testing --- uuid_source.go | 30 ++++++++++++++++++++++-------- uuid_source_test.go | 44 +++++++++++++++++++++++++++++++++++++------- uuid_test.go | 21 +++++++++++++++++++++ version4.go | 7 ++++++- 4 files changed, 86 insertions(+), 16 deletions(-) diff --git a/uuid_source.go b/uuid_source.go index 211b052..83a569c 100644 --- a/uuid_source.go +++ b/uuid_source.go @@ -5,10 +5,18 @@ 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) @@ -16,6 +24,12 @@ func NewSource(r io.Reader) 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 @@ -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()) } diff --git a/uuid_source_test.go b/uuid_source_test.go index 646e605..762146d 100644 --- a/uuid_source_test.go +++ b/uuid_source_test.go @@ -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++ { - 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) + } + } + + // 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++ { + uuid1 := uuidSourceA.New() + uuid2 := uuidSourceB.New() + if uuid1 == uuid2 { + t.Errorf("unexpected duplicates, got %q", uuid1) } } diff --git a/uuid_test.go b/uuid_test.go index e7876f1..ff17ff3 100644 --- a/uuid_test.go +++ b/uuid_test.go @@ -13,6 +13,8 @@ import ( "testing" "time" "unsafe" + + "math/rand" ) type test struct { @@ -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) diff --git a/version4.go b/version4.go index 84af91c..66acd62 100644 --- a/version4.go +++ b/version4.go @@ -27,8 +27,12 @@ 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 } @@ -36,3 +40,4 @@ func NewRandom() (UUID, error) { uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10 return uuid, nil } +