// Copyright 2025 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package uuid_test import ( "encoding/binary" "testing" "testing/synctest" "time" "uuid" ) func TestNew(t *testing.T) { for _, test := range []struct { name string newf func() uuid.UUID version byte variant byte }{{ name: "New", newf: uuid.New, version: 4, variant: 0b10, }, { name: "NewV4", newf: uuid.NewV4, version: 4, variant: 0b10, }, { name: "NewV7", newf: uuid.NewV7, version: 7, variant: 0b10, }} { u := test.newf() if got, want := version(u), test.version; got != want { t.Errorf("%v: got version %v, want %v", test.name, got, want) } if got, want := variant(u), test.variant; got != want { t.Errorf("%v: got variant %v, want %v", test.name, got, want) } } } func version(u uuid.UUID) byte { return u[6] >> 4 } func variant(u uuid.UUID) byte { return u[8] >> 6 } func TestNewV7Millis(t *testing.T) { // Verify the unix_ts_ms field of a UUIDv7 is set correctly. check := func(t *testing.T) { t.Helper() u := uuid.NewV7() got := binary.BigEndian.Uint64(u[:8]) >> 16 want := uint64(time.Now().UnixMilli()) if got != want { t.Errorf("at %v, NewV7() = %v; millis = %x, want %x", time.Now(), u, got, want) } } synctest.Test(t, func(t *testing.T) { check(t) time.Sleep(1 * time.Hour) check(t) time.Sleep(time.Second - 1*time.Nanosecond) // maximum fractional seconds check(t) time.Sleep(2 * time.Nanosecond) // minimum fractional seconds check(t) }) // Testing in a new bubble causes time to go backwards. // UUIDs should use the new time. synctest.Test(t, func(t *testing.T) { check(t) }) } func TestNewV7Collision(t *testing.T) { // Verify UUIDv7s generated at the same instant do not collide and // are monotonically increasing. synctest.Test(t, func(t *testing.T) { last := uuid.NewV7() for range 3 { // Enough iterations to overflow the fractional millisecond component // several times. for range (1 << 12) * 3 { u := uuid.NewV7() if u.Compare(last) != 1 { t.Fatalf("NewV7 returned UUIDs out of order:\nprevious: %v\n current: %v", last, u) } last = u } // Time advances, but not as quickly as we are generating UUIDs. time.Sleep(1 * time.Millisecond) } }) } func TestEncode(t *testing.T) { u := uuid.UUID{0xf8, 0x1d, 0x4f, 0xae, 0x7d, 0xec, 0x11, 0xd0, 0xa7, 0x65, 0x00, 0xa0, 0xc9, 0x1e, 0x6b, 0xf6} want := "f81d4fae-7dec-11d0-a765-00a0c91e6bf6" if got := u.String(); got != want { t.Errorf("u.String() = %q, want %q", got, want) } if got, err := u.MarshalText(); string(got) != want || err != nil { t.Errorf("u.MarshalText() = %q, %v; want %q, nil", got, err, want) } prefix := []byte("urn:uuid:") if got, err := u.AppendText(prefix); string(got) != string(prefix)+want || err != nil { t.Errorf("u.MarshalAppend(%q) = %q, %v; want %q, nil", prefix, got, err, string(prefix)+want) } } func TestUnmarshalText(t *testing.T) { var got uuid.UUID err := got.UnmarshalText([]byte("f81d4fae-7dec-11d0-a765-00a0c91e6bf6")) if err != nil { t.Errorf("UnmarshalText: %v", err) } want := uuid.UUID{0xf8, 0x1d, 0x4f, 0xae, 0x7d, 0xec, 0x11, 0xd0, 0xa7, 0x65, 0x00, 0xa0, 0xc9, 0x1e, 0x6b, 0xf6} if got != want { t.Errorf("got %q, want %q", got, want) } } func TestParseSuccess(t *testing.T) { u1 := uuid.UUID{ 0xf8, 0x1d, 0x4f, 0xae, 0x7d, 0xec, 0x11, 0xd0, 0xa7, 0x65, 0x00, 0xa0, 0xc9, 0x1e, 0x6b, 0xf6, } for _, test := range []struct { s string u uuid.UUID }{{ s: "00000000-0000-0000-0000-000000000000", u: uuid.Nil(), }, { s: "ffffffff-ffff-ffff-ffff-ffffffffffff", u: uuid.Max(), }, { s: "f81d4fae-7dec-11d0-a765-00a0c91e6bf6", u: u1, }, { s: "F81D4FAE-7DEC-11D0-A765-00A0C91E6BF6", u: u1, }, { s: "f81d4fae7dec11d0a76500a0c91e6bf6", u: u1, }, { s: "{f81d4fae-7dec-11d0-a765-00a0c91e6bf6}", u: u1, }, { s: "urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6", u: u1, }} { u, err := uuid.Parse(test.s) if err != nil { t.Errorf("Parse(%q) = _, %v; want success", test.s, err) } else if u != test.u { t.Errorf("Parse(%q) = %v, nil; want %v", test.s, u, test.u) } } } func TestParseErrors(t *testing.T) { for _, test := range []string{ "", "0000000000000-0000-0000-000000000000", "00000000-000000000-0000-000000000000", "00000000-0000-000000000-000000000000", "00000000-0000-0000-00000000000000000", "00000000-0000-0000-0000-00000000000", "x0000000-0000-0000-0000-000000000000", "00000000-x000-0000-0000-000000000000", "00000000-0000-x000-0000-000000000000", "00000000-0000-0000-x000-000000000000", "00000000-0000-0000-0000-x00000000000", "{x0000000-0000-0000-0000-000000000000}", "urn:uuid:x000000-0000-0000-0000-000000000000", "x0000000000000000000000000000000", // Some parsers permit hyphens in non-standard locations, // but we currently do not. "0000-0000-0000-0000-0000-0000-0000-0000", // Combinations of variant encodings that we could choose to parse, // but currently do not. "{00000000000000000000000000000000}", "{urn:uuid:00000000-0000-0000-0000-000000000000}", "urn:uuid:00000000000000000000000000000000", } { got, err := uuid.Parse(test) if err == nil { t.Errorf("Parse(%q) = %v, nil; want error", test, got) } } } func TestCompare(t *testing.T) { uuids := []uuid.UUID{ uuid.Nil(), uuid.MustParse("f81d4fae-7dec-11d0-a765-00a0c91e6bf6"), uuid.Max(), } for i, u := range uuids { if got, want := u.Compare(u), 0; got != want { t.Errorf("%v.Compare(itself) = %v, want %v", u, got, want) } if i == 0 { continue } prev := uuids[i-1] if got, want := u.Compare(prev), 1; got != want { t.Errorf("%v.Compare(%v) = %v, want %v", u, prev, got, want) } if got, want := prev.Compare(u), -1; got != want { t.Errorf("%v.Compare(%v) = %v, want %v", prev, u, got, want) } } } func BenchmarkNewV4(b *testing.B) { for b.Loop() { uuid.NewV4() } } func BenchmarkNewV7(b *testing.B) { for b.Loop() { uuid.NewV7() } } func BenchmarkString(b *testing.B) { u := uuid.MustParse("f81d4fae-7dec-11d0-a765-00a0c91e6bf6") for b.Loop() { _ = u.String() } } func BenchmarkParseSuccess(b *testing.B) { for b.Loop() { uuid.Parse("f81d4fae-7dec-11d0-a765-00a0c91e6bf6") } } func BenchmarkParseError(b *testing.B) { for b.Loop() { uuid.Parse("00000000-0000-0000-0000-00000000000X") } }