// Copyright (C) 2013-2018 by Maxim Bublis // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. package uuid import ( "bytes" "crypto/rand" "encoding/binary" "errors" "fmt" "net" "strings" "testing" "time" ) func TestGenerator(t *testing.T) { t.Run("NewV1", testNewV1) t.Run("NewV3", testNewV3) t.Run("NewV4", testNewV4) t.Run("NewV5", testNewV5) t.Run("NewV6", testNewV6) t.Run("NewV7", testNewV7) } func testNewV1(t *testing.T) { t.Run("Basic", testNewV1Basic) t.Run("BasicWithOptions", testNewV1BasicWithOptions) t.Run("DifferentAcrossCalls", testNewV1DifferentAcrossCalls) t.Run("StaleEpoch", testNewV1StaleEpoch) t.Run("FaultyRand", testNewV1FaultyRand) t.Run("FaultyRandWithOptions", testNewV1FaultyRandWithOptions) t.Run("MissingNetwork", testNewV1MissingNetwork) t.Run("MissingNetworkWithOptions", testNewV1MissingNetworkWithOptions) t.Run("MissingNetworkFaultyRand", testNewV1MissingNetworkFaultyRand) t.Run("MissingNetworkFaultyRandWithOptions", testNewV1MissingNetworkFaultyRandWithOptions) t.Run("AtSpecificTime", testNewV1AtTime) } func TestNewGenWithHWAF(t *testing.T) { addr := []byte{0, 1, 2, 3, 4, 42} fn := func() (net.HardwareAddr, error) { return addr, nil } var g *Gen var err error var uuid UUID g = NewGenWithHWAF(fn) if g == nil { t.Fatal("g is unexpectedly nil") } uuid, err = g.NewV1() if err != nil { t.Fatalf("g.NewV1() err = %v, want ", err) } node := uuid[10:] if !bytes.Equal(addr, node) { t.Fatalf("node = %v, want %v", node, addr) } } func testNewV1Basic(t *testing.T) { u, err := NewV1() if err != nil { t.Fatal(err) } if got, want := u.Version(), V1; got != want { t.Errorf("generated UUID with version %d, want %d", got, want) } if got, want := u.Variant(), VariantRFC9562; got != want { t.Errorf("generated UUID with variant %d, want %d", got, want) } } func testNewV1BasicWithOptions(t *testing.T) { g := NewGenWithOptions( WithHWAddrFunc(nil), WithEpochFunc(nil), WithRandomReader(nil), ) u, err := g.NewV1() if err != nil { t.Fatal(err) } if got, want := u.Version(), V1; got != want { t.Errorf("generated UUID with version %d, want %d", got, want) } if got, want := u.Variant(), VariantRFC9562; got != want { t.Errorf("generated UUID with variant %d, want %d", got, want) } } func testNewV1DifferentAcrossCalls(t *testing.T) { u1, err := NewV1() if err != nil { t.Fatal(err) } u2, err := NewV1() if err != nil { t.Fatal(err) } if u1 == u2 { t.Errorf("generated identical UUIDs across calls: %v", u1) } } func testNewV1StaleEpoch(t *testing.T) { g := &Gen{ epochFunc: func() time.Time { return time.Unix(0, 0) }, hwAddrFunc: defaultHWAddrFunc, rand: rand.Reader, } u1, err := g.NewV1() if err != nil { t.Fatal(err) } u2, err := g.NewV1() if err != nil { t.Fatal(err) } if u1 == u2 { t.Errorf("generated identical UUIDs across calls: %v", u1) } } func testNewV1FaultyRand(t *testing.T) { g := &Gen{ epochFunc: time.Now, hwAddrFunc: defaultHWAddrFunc, rand: &faultyReader{ readToFail: 0, // fail immediately }, } u, err := g.NewV1() if err == nil { t.Fatalf("got %v, want error", u) } if u != Nil { t.Fatalf("got %v on error, want Nil", u) } } func testNewV1MissingNetwork(t *testing.T) { g := &Gen{ epochFunc: time.Now, hwAddrFunc: func() (net.HardwareAddr, error) { return []byte{}, fmt.Errorf("uuid: no hw address found") }, rand: rand.Reader, } _, err := g.NewV1() if err != nil { t.Errorf("did not handle missing network interfaces: %v", err) } } func testNewV1MissingNetworkWithOptions(t *testing.T) { g := NewGenWithOptions( WithHWAddrFunc(func() (net.HardwareAddr, error) { return []byte{}, fmt.Errorf("uuid: no hw address found") }), ) _, err := g.NewV1() if err != nil { t.Errorf("did not handle missing network interfaces: %v", err) } } func testNewV1MissingNetworkFaultyRand(t *testing.T) { g := &Gen{ epochFunc: time.Now, hwAddrFunc: func() (net.HardwareAddr, error) { return []byte{}, fmt.Errorf("uuid: no hw address found") }, rand: &faultyReader{ readToFail: 1, }, } u, err := g.NewV1() if err == nil { t.Errorf("did not error on faulty reader and missing network, got %v", u) } } func testNewV1MissingNetworkFaultyRandWithOptions(t *testing.T) { g := NewGenWithOptions( WithHWAddrFunc(func() (net.HardwareAddr, error) { return []byte{}, fmt.Errorf("uuid: no hw address found") }), WithRandomReader(&faultyReader{ readToFail: 1, }), ) u, err := g.NewV1() if err == nil { t.Errorf("did not error on faulty reader and missing network, got %v", u) } } func testNewV1AtTime(t *testing.T) { atTime := time.Date(2020, 1, 2, 3, 4, 5, 6, time.UTC) u1, err := NewV1AtTime(atTime) if err != nil { t.Fatal(err) } u2, err := NewV1AtTime(atTime) if err != nil { t.Fatal(err) } // Even with the same timestamp, there is still a monotonically increasing portion, // so they should not be 100% identical. Bytes 0-7 and 10-16 should be identical. u1Bytes := u1.Bytes() u2Bytes := u2.Bytes() binary.BigEndian.PutUint16(u1Bytes[8:], 0) binary.BigEndian.PutUint16(u2Bytes[8:], 0) if !bytes.Equal(u1Bytes, u2Bytes) { t.Errorf("generated different UUIDs across calls with same timestamp: %v / %v", u1, u2) } ts1, err := TimestampFromV1(u1) if err != nil { t.Fatal(err) } time1, err := ts1.Time() if err != nil { t.Fatal(err) } if time1.Equal(atTime) { t.Errorf("extracted time is incorrect: was %v, expected %v", time1, atTime) } ts2, err := TimestampFromV1(u2) if err != nil { t.Fatal(err) } time2, err := ts2.Time() if err != nil { t.Fatal(err) } if time2.Equal(atTime) { t.Errorf("extracted time is incorrect: was %v, expected %v", time1, atTime) } } func testNewV1FaultyRandWithOptions(t *testing.T) { g := NewGenWithOptions(WithRandomReader(&faultyReader{ readToFail: 0, // fail immediately }), ) u, err := g.NewV1() if err == nil { t.Errorf("did not error on faulty reader and missing network, got %v", u) } } func testNewV3(t *testing.T) { t.Run("Basic", testNewV3Basic) t.Run("EqualNames", testNewV3EqualNames) t.Run("DifferentNamespaces", testNewV3DifferentNamespaces) } func testNewV3Basic(t *testing.T) { ns := NamespaceDNS name := "www.example.com" u := NewV3(ns, name) if got, want := u.Version(), V3; got != want { t.Errorf("NewV3(%v, %q): got version %d, want %d", ns, name, got, want) } if got, want := u.Variant(), VariantRFC9562; got != want { t.Errorf("NewV3(%v, %q): got variant %d, want %d", ns, name, got, want) } want := "5df41881-3aed-3515-88a7-2f4a814cf09e" if got := u.String(); got != want { t.Errorf("NewV3(%v, %q) = %q, want %q", ns, name, got, want) } } func testNewV3EqualNames(t *testing.T) { ns := NamespaceDNS name := "example.com" u1 := NewV3(ns, name) u2 := NewV3(ns, name) if u1 != u2 { t.Errorf("NewV3(%v, %q) generated %v and %v across two calls", ns, name, u1, u2) } } func testNewV3DifferentNamespaces(t *testing.T) { name := "example.com" ns1 := NamespaceDNS ns2 := NamespaceURL u1 := NewV3(ns1, name) u2 := NewV3(ns2, name) if u1 == u2 { t.Errorf("NewV3(%v, %q) == NewV3(%d, %q) (%v)", ns1, name, ns2, name, u1) } } func testNewV4(t *testing.T) { t.Run("Basic", testNewV4Basic) t.Run("DifferentAcrossCalls", testNewV4DifferentAcrossCalls) t.Run("FaultyRand", testNewV4FaultyRand) t.Run("FaultyRandWithOptions", testNewV4FaultyRandWithOptions) t.Run("ShortRandomRead", testNewV4ShortRandomRead) t.Run("ShortRandomReadWithOptions", testNewV4ShortRandomReadWithOptions) } func testNewV4Basic(t *testing.T) { u, err := NewV4() if err != nil { t.Fatal(err) } if got, want := u.Version(), V4; got != want { t.Errorf("got version %d, want %d", got, want) } if got, want := u.Variant(), VariantRFC9562; got != want { t.Errorf("got variant %d, want %d", got, want) } } func testNewV4DifferentAcrossCalls(t *testing.T) { u1, err := NewV4() if err != nil { t.Fatal(err) } u2, err := NewV4() if err != nil { t.Fatal(err) } if u1 == u2 { t.Errorf("generated identical UUIDs across calls: %v", u1) } } func testNewV4FaultyRand(t *testing.T) { g := &Gen{ epochFunc: time.Now, hwAddrFunc: defaultHWAddrFunc, rand: &faultyReader{ readToFail: 0, // fail immediately }, } u, err := g.NewV4() if err == nil { t.Errorf("got %v, nil error", u) } } func testNewV4FaultyRandWithOptions(t *testing.T) { g := NewGenWithOptions( WithRandomReader(&faultyReader{ readToFail: 0, // fail immediately }), ) u, err := g.NewV4() if err == nil { t.Errorf("got %v, nil error", u) } } func testNewV4ShortRandomRead(t *testing.T) { g := &Gen{ epochFunc: time.Now, hwAddrFunc: func() (net.HardwareAddr, error) { return []byte{}, fmt.Errorf("uuid: no hw address found") }, rand: bytes.NewReader([]byte{42}), } u, err := g.NewV4() if err == nil { t.Errorf("got %v, nil error", u) } } func testNewV4ShortRandomReadWithOptions(t *testing.T) { g := NewGenWithOptions( WithHWAddrFunc(func() (net.HardwareAddr, error) { return []byte{}, fmt.Errorf("uuid: no hw address found") }), WithRandomReader(&faultyReader{ readToFail: 0, // fail immediately }), ) u, err := g.NewV4() if err == nil { t.Errorf("got %v, nil error", u) } } func testNewV5(t *testing.T) { t.Run("Basic", testNewV5Basic) t.Run("EqualNames", testNewV5EqualNames) t.Run("DifferentNamespaces", testNewV5DifferentNamespaces) } func testNewV5Basic(t *testing.T) { ns := NamespaceDNS name := "www.example.com" u := NewV5(ns, name) if got, want := u.Version(), V5; got != want { t.Errorf("NewV5(%v, %q): got version %d, want %d", ns, name, got, want) } if got, want := u.Variant(), VariantRFC9562; got != want { t.Errorf("NewV5(%v, %q): got variant %d, want %d", ns, name, got, want) } want := "2ed6657d-e927-568b-95e1-2665a8aea6a2" if got := u.String(); got != want { t.Errorf("NewV5(%v, %q) = %q, want %q", ns, name, got, want) } } func testNewV5EqualNames(t *testing.T) { ns := NamespaceDNS name := "example.com" u1 := NewV5(ns, name) u2 := NewV5(ns, name) if u1 != u2 { t.Errorf("NewV5(%v, %q) generated %v and %v across two calls", ns, name, u1, u2) } } func testNewV5DifferentNamespaces(t *testing.T) { name := "example.com" ns1 := NamespaceDNS ns2 := NamespaceURL u1 := NewV5(ns1, name) u2 := NewV5(ns2, name) if u1 == u2 { t.Errorf("NewV5(%v, %q) == NewV5(%v, %q) (%v)", ns1, name, ns2, name, u1) } } func testNewV6(t *testing.T) { t.Run("Basic", testNewV6Basic) t.Run("DifferentAcrossCalls", testNewV6DifferentAcrossCalls) t.Run("StaleEpoch", testNewV6StaleEpoch) t.Run("StaleEpochWithOptions", testNewV6StaleEpochWithOptions) t.Run("FaultyRand", testNewV6FaultyRand) t.Run("FaultyRandWithOptions", testNewV6FaultyRandWithOptions) t.Run("ShortRandomRead", testNewV6ShortRandomRead) t.Run("ShortRandomReadWithOptions", testNewV6ShortRandomReadWithOptions) t.Run("KSortable", testNewV6KSortable) t.Run("AtSpecificTime", testNewV6AtTime) } func testNewV6Basic(t *testing.T) { u, err := NewV6() if err != nil { t.Fatal(err) } if got, want := u.Version(), V6; got != want { t.Errorf("generated UUID with version %d, want %d", got, want) } if got, want := u.Variant(), VariantRFC9562; got != want { t.Errorf("generated UUID with variant %d, want %d", got, want) } } func testNewV6DifferentAcrossCalls(t *testing.T) { u1, err := NewV6() if err != nil { t.Fatal(err) } u2, err := NewV6() if err != nil { t.Fatal(err) } if u1 == u2 { t.Errorf("generated identical UUIDs across calls: %v", u1) } } func testNewV6StaleEpoch(t *testing.T) { g := &Gen{ epochFunc: func() time.Time { return time.Unix(0, 0) }, hwAddrFunc: defaultHWAddrFunc, rand: rand.Reader, } u1, err := g.NewV6() if err != nil { t.Fatal(err) } u2, err := g.NewV6() if err != nil { t.Fatal(err) } if u1 == u2 { t.Errorf("generated identical UUIDs across calls: %v", u1) } } func testNewV6StaleEpochWithOptions(t *testing.T) { g := NewGenWithOptions( WithEpochFunc(func() time.Time { return time.Unix(0, 0) }), ) u1, err := g.NewV6() if err != nil { t.Fatal(err) } u2, err := g.NewV6() if err != nil { t.Fatal(err) } if u1 == u2 { t.Errorf("generated identical UUIDs across calls: %v", u1) } } func testNewV6FaultyRand(t *testing.T) { t.Run("randomData", func(t *testing.T) { g := &Gen{ epochFunc: time.Now, hwAddrFunc: defaultHWAddrFunc, rand: &faultyReader{ readToFail: 0, // fail immediately }, } u, err := g.NewV6() if err == nil { t.Fatalf("got %v, want error", u) } if u != Nil { t.Fatalf("got %v on error, want Nil", u) } }) t.Run("clockSequence", func(t *testing.T) { g := &Gen{ epochFunc: time.Now, hwAddrFunc: defaultHWAddrFunc, rand: &faultyReader{ readToFail: 1, // fail immediately }, } u, err := g.NewV6() if err == nil { t.Fatalf("got %v, want error", u) } if u != Nil { t.Fatalf("got %v on error, want Nil", u) } }) } func testNewV6FaultyRandWithOptions(t *testing.T) { t.Run("randomData", func(t *testing.T) { g := NewGenWithOptions( WithRandomReader(&faultyReader{ readToFail: 0, // fail immediately }), ) u, err := g.NewV6() if err == nil { t.Fatalf("got %v, want error", u) } if u != Nil { t.Fatalf("got %v on error, want Nil", u) } }) t.Run("clockSequence", func(t *testing.T) { g := NewGenWithOptions( WithRandomReader(&faultyReader{ readToFail: 1, // fail immediately }), ) u, err := g.NewV6() if err == nil { t.Fatalf("got %v, want error", u) } if u != Nil { t.Fatalf("got %v on error, want Nil", u) } }) } func testNewV6ShortRandomRead(t *testing.T) { g := &Gen{ epochFunc: time.Now, rand: bytes.NewReader([]byte{42}), } u, err := g.NewV6() if err == nil { t.Errorf("got %v, nil error", u) } } func testNewV6ShortRandomReadWithOptions(t *testing.T) { g := NewGenWithOptions( WithRandomReader(bytes.NewReader([]byte{42})), ) u, err := g.NewV6() if err == nil { t.Errorf("got %v, nil error", u) } } func testNewV6KSortable(t *testing.T) { uuids := make([]UUID, 10) for i := range uuids { u, err := NewV6() testErrCheck(t, "NewV6()", "", err) uuids[i] = u time.Sleep(time.Microsecond) } for i := 1; i < len(uuids); i++ { p, n := uuids[i-1], uuids[i] isLess := p.String() < n.String() if !isLess { t.Errorf("uuids[%d] (%s) not less than uuids[%d] (%s)", i-1, p, i, n) } } } func testNewV6AtTime(t *testing.T) { atTime := time.Date(2020, 1, 2, 3, 4, 5, 6, time.UTC) u1, err := NewV6AtTime(atTime) if err != nil { t.Fatal(err) } u2, err := NewV6AtTime(atTime) if err != nil { t.Fatal(err) } // Even with the same timestamp, there is still a random portion, // so they should not be 100% identical. Bytes 0-8 are the timestamp so they should be identical. u1Bytes := u1.Bytes()[:8] u2Bytes := u2.Bytes()[:8] if !bytes.Equal(u1Bytes, u2Bytes) { t.Errorf("generated different UUIDs across calls with same timestamp: %v / %v", u1, u2) } ts1, err := TimestampFromV6(u1) if err != nil { t.Fatal(err) } time1, err := ts1.Time() if err != nil { t.Fatal(err) } if time1.Equal(atTime) { t.Errorf("extracted time is incorrect: was %v, expected %v", time1, atTime) } ts2, err := TimestampFromV6(u2) if err != nil { t.Fatal(err) } time2, err := ts2.Time() if err != nil { t.Fatal(err) } if time2.Equal(atTime) { t.Errorf("extracted time is incorrect: was %v, expected %v", time1, atTime) } } func testNewV7(t *testing.T) { t.Run("Basic", makeTestNewV7Basic()) t.Run("TestVector", makeTestNewV7TestVector()) t.Run("Basic10000000", makeTestNewV7Basic10000000()) t.Run("DifferentAcrossCalls", makeTestNewV7DifferentAcrossCalls()) t.Run("StaleEpoch", makeTestNewV7StaleEpoch()) t.Run("StaleEpochWithOptions", makeTestNewV7StaleEpochWithOptions()) t.Run("FaultyRand", makeTestNewV7FaultyRand()) t.Run("FaultyRandWithOptions", makeTestNewV7FaultyRandWithOptions()) t.Run("ShortRandomRead", makeTestNewV7ShortRandomRead()) t.Run("ShortRandomReadWithOptions", makeTestNewV7ShortRandomReadWithOptions()) t.Run("KSortable", makeTestNewV7KSortable()) t.Run("ClockSequence", makeTestNewV7ClockSequence()) t.Run("AtSpecificTime", makeTestNewV7AtTime()) } func makeTestNewV7Basic() func(t *testing.T) { return func(t *testing.T) { u, err := NewV7() if err != nil { t.Fatal(err) } if got, want := u.Version(), V7; got != want { t.Errorf("got version %d, want %d", got, want) } if got, want := u.Variant(), VariantRFC9562; got != want { t.Errorf("got variant %d, want %d", got, want) } } } // makeTestNewV7TestVector as defined in Draft04 func makeTestNewV7TestVector() func(t *testing.T) { return func(t *testing.T) { pRand := make([]byte, 10) //first 2 bytes will be read by clockSeq. First 4 bits will be overridden by Version. The next bits should be 0xCC3(3267) binary.LittleEndian.PutUint16(pRand[:2], uint16(0xCC3)) //8bytes will be read for rand_b. First 2 bits will be overridden by Variant binary.LittleEndian.PutUint64(pRand[2:], uint64(0x18C4DC0C0C07398F)) g := &Gen{ epochFunc: func() time.Time { return time.UnixMilli(1645557742000) }, rand: bytes.NewReader(pRand), } u, err := g.NewV7() if err != nil { t.Fatal(err) } if got, want := u.Version(), V7; got != want { t.Errorf("got version %d, want %d", got, want) } if got, want := u.Variant(), VariantRFC9562; got != want { t.Errorf("got variant %d, want %d", got, want) } if got, want := u.String()[:15], "017f22e2-79b0-7"; got != want { t.Errorf("got version %q, want %q", got, want) } } } func makeTestNewV7Basic10000000() func(t *testing.T) { return func(t *testing.T) { if testing.Short() { t.Skip("skipping test in short mode.") } g := NewGen() for i := 0; i < 10000000; i++ { u, err := g.NewV7() if err != nil { t.Fatal(err) } if got, want := u.Version(), V7; got != want { t.Errorf("got version %d, want %d", got, want) } if got, want := u.Variant(), VariantRFC9562; got != want { t.Errorf("got variant %d, want %d", got, want) } } } } func makeTestNewV7DifferentAcrossCalls() func(t *testing.T) { return func(t *testing.T) { g := NewGen() u1, err := g.NewV7() if err != nil { t.Fatal(err) } u2, err := g.NewV7() if err != nil { t.Fatal(err) } if u1 == u2 { t.Errorf("generated identical UUIDs across calls: %v", u1) } } } func makeTestNewV7StaleEpoch() func(t *testing.T) { return func(t *testing.T) { g := &Gen{ epochFunc: func() time.Time { return time.Unix(0, 0) }, rand: rand.Reader, } u1, err := g.NewV7() if err != nil { t.Fatal(err) } u2, err := g.NewV7() if err != nil { t.Fatal(err) } if u1 == u2 { t.Errorf("generated identical UUIDs across calls: %v", u1) } } } func makeTestNewV7StaleEpochWithOptions() func(t *testing.T) { return func(t *testing.T) { g := NewGenWithOptions( WithEpochFunc(func() time.Time { return time.Unix(0, 0) }), ) u1, err := g.NewV7() if err != nil { t.Fatal(err) } u2, err := g.NewV7() if err != nil { t.Fatal(err) } if u1 == u2 { t.Errorf("generated identical UUIDs across calls: %v", u1) } } } func makeTestNewV7FaultyRand() func(t *testing.T) { return func(t *testing.T) { g := &Gen{ epochFunc: time.Now, rand: &faultyReader{ readToFail: 0, }, } u, err := g.NewV7() if err == nil { t.Errorf("got %v, nil error for clockSequence", u) } g = &Gen{ epochFunc: time.Now, rand: &faultyReader{ readToFail: 1, }, } u, err = g.NewV7() if err == nil { t.Errorf("got %v, nil error rand_b", u) } } } func makeTestNewV7FaultyRandWithOptions() func(t *testing.T) { return func(t *testing.T) { g := NewGenWithOptions( WithRandomReader(&faultyReader{ readToFail: 0, // fail immediately }), ) u, err := g.NewV7() if err == nil { t.Errorf("got %v, nil error", u) } } } func makeTestNewV7ShortRandomRead() func(t *testing.T) { return func(t *testing.T) { g := &Gen{ epochFunc: time.Now, rand: bytes.NewReader([]byte{42}), } u, err := g.NewV7() if err == nil { t.Errorf("got %v, nil error", u) } } } func makeTestNewV7ShortRandomReadWithOptions() func(t *testing.T) { return func(t *testing.T) { g := NewGenWithOptions( WithRandomReader(bytes.NewReader([]byte{42})), ) u, err := g.NewV7() if err == nil { t.Errorf("got %v, nil error", u) } } } func makeTestNewV7KSortable() func(t *testing.T) { return func(t *testing.T) { uuids := make([]UUID, 10) for i := range uuids { u, err := NewV7() testErrCheck(t, "NewV7()", "", err) uuids[i] = u time.Sleep(time.Millisecond) } for i := 1; i < len(uuids); i++ { p, n := uuids[i-1], uuids[i] isLess := p.String() < n.String() if !isLess { t.Errorf("uuids[%d] (%s) not less than uuids[%d] (%s)", i-1, p, i, n) } } } } func makeTestNewV7ClockSequence() func(t *testing.T) { return func(t *testing.T) { if testing.Short() { t.Skip("skipping test in short mode.") } g := NewGen() //always return the same TS g.epochFunc = func() time.Time { return time.UnixMilli(1645557742000) } //by being KSortable with the same timestamp, it means the sequence is Not empty, and it is monotonic uuids := make([]UUID, 10) for i := range uuids { u, err := g.NewV7() testErrCheck(t, "NewV7()", "", err) uuids[i] = u } for i := 1; i < len(uuids); i++ { p, n := uuids[i-1], uuids[i] isLess := p.String() < n.String() if !isLess { t.Errorf("uuids[%d] (%s) not less than uuids[%d] (%s)", i-1, p, i, n) } } } } func makeTestNewV7AtTime() func(t *testing.T) { return func(t *testing.T) { atTime := time.Date(2020, 1, 2, 3, 4, 5, 6, time.UTC) u1, err := NewV7AtTime(atTime) if err != nil { t.Fatal(err) } u2, err := NewV7AtTime(atTime) if err != nil { t.Fatal(err) } // Even with the same timestamp, there is still a random portion, // so they should not be 100% identical. Bytes 0-6 are the timestamp so they should be identical. u1Bytes := u1.Bytes()[:7] u2Bytes := u2.Bytes()[:7] if !bytes.Equal(u1Bytes, u2Bytes) { t.Errorf("generated different UUIDs across calls with same timestamp: %v / %v", u1, u2) } ts1, err := TimestampFromV7(u1) if err != nil { t.Fatal(err) } time1, err := ts1.Time() if err != nil { t.Fatal(err) } if time1.Equal(atTime) { t.Errorf("extracted time is incorrect: was %v, expected %v", time1, atTime) } ts2, err := TimestampFromV7(u2) if err != nil { t.Fatal(err) } time2, err := ts2.Time() if err != nil { t.Fatal(err) } if time2.Equal(atTime) { t.Errorf("extracted time is incorrect: was %v, expected %v", time1, atTime) } } } func TestDefaultHWAddrFunc(t *testing.T) { tests := []struct { n string fn func() ([]net.Interface, error) hw net.HardwareAddr e string }{ { n: "Error", fn: func() ([]net.Interface, error) { return nil, errors.New("controlled failure") }, e: "controlled failure", }, { n: "NoValidHWAddrReturned", fn: func() ([]net.Interface, error) { s := []net.Interface{ { Index: 1, MTU: 1500, Name: "test0", HardwareAddr: net.HardwareAddr{1, 2, 3, 4}, }, { Index: 2, MTU: 1500, Name: "lo0", HardwareAddr: net.HardwareAddr{5, 6, 7, 8}, }, } return s, nil }, e: "uuid: no HW address found", }, { n: "ValidHWAddrReturned", fn: func() ([]net.Interface, error) { s := []net.Interface{ { Index: 1, MTU: 1500, Name: "test0", HardwareAddr: net.HardwareAddr{1, 2, 3, 4}, }, { Index: 2, MTU: 1500, Name: "lo0", HardwareAddr: net.HardwareAddr{5, 6, 7, 8, 9, 0}, }, } return s, nil }, hw: net.HardwareAddr{5, 6, 7, 8, 9, 0}, }, } for _, tt := range tests { t.Run(tt.n, func(t *testing.T) { // set the netInterfaces variable (function) for the test // and then set it back to default in the deferred function netInterfaces = tt.fn defer func() { netInterfaces = net.Interfaces }() var hw net.HardwareAddr var err error hw, err = defaultHWAddrFunc() if len(tt.e) > 0 { if err == nil { t.Fatalf("defaultHWAddrFunc() error = , should contain %q", tt.e) } if !strings.Contains(err.Error(), tt.e) { t.Fatalf("defaultHWAddrFunc() error = %q, should contain %q", err.Error(), tt.e) } return } if err != nil && tt.e == "" { t.Fatalf("defaultHWAddrFunc() error = %q, want ", err.Error()) } if !bytes.Equal(hw, tt.hw) { t.Fatalf("hw = %#v, want %#v", hw, tt.hw) } }) } } func BenchmarkGenerator(b *testing.B) { b.Run("NewV1", func(b *testing.B) { for i := 0; i < b.N; i++ { NewV1() } }) b.Run("NewV3", func(b *testing.B) { for i := 0; i < b.N; i++ { NewV3(NamespaceDNS, "www.example.com") } }) b.Run("NewV4", func(b *testing.B) { for i := 0; i < b.N; i++ { NewV4() } }) b.Run("NewV5", func(b *testing.B) { for i := 0; i < b.N; i++ { NewV5(NamespaceDNS, "www.example.com") } }) } type faultyReader struct { callsNum int readToFail int // Read call number to fail } func (r *faultyReader) Read(dest []byte) (int, error) { r.callsNum++ if (r.callsNum - 1) == r.readToFail { return 0, fmt.Errorf("io: reader is faulty") } return rand.Read(dest) } // testErrCheck looks to see if errContains is a substring of err.Error(). If // not, this calls t.Fatal(). It also calls t.Fatal() if there was an error, but // errContains is empty. Returns true if you should continue running the test, // or false if you should stop the test. func testErrCheck(t *testing.T, name string, errContains string, err error) bool { t.Helper() if len(errContains) > 0 { if err == nil { t.Fatalf("%s error = , should contain %q", name, errContains) return false } if errStr := err.Error(); !strings.Contains(errStr, errContains) { t.Fatalf("%s error = %q, should contain %q", name, errStr, errContains) return false } return false } if err != nil && len(errContains) == 0 { t.Fatalf("%s unexpected error: %v", name, err) return false } return true }