Source file src/uuid/uuid_test.go

     1  // Copyright 2025 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package uuid_test
     6  
     7  import (
     8  	"encoding/binary"
     9  	"testing"
    10  	"testing/synctest"
    11  	"time"
    12  	"uuid"
    13  )
    14  
    15  func TestNew(t *testing.T) {
    16  	for _, test := range []struct {
    17  		name    string
    18  		newf    func() uuid.UUID
    19  		version byte
    20  		variant byte
    21  	}{{
    22  		name:    "New",
    23  		newf:    uuid.New,
    24  		version: 4,
    25  		variant: 0b10,
    26  	}, {
    27  		name:    "NewV4",
    28  		newf:    uuid.NewV4,
    29  		version: 4,
    30  		variant: 0b10,
    31  	}, {
    32  		name:    "NewV7",
    33  		newf:    uuid.NewV7,
    34  		version: 7,
    35  		variant: 0b10,
    36  	}} {
    37  		u := test.newf()
    38  		if got, want := version(u), test.version; got != want {
    39  			t.Errorf("%v: got version %v, want %v", test.name, got, want)
    40  		}
    41  		if got, want := variant(u), test.variant; got != want {
    42  			t.Errorf("%v: got variant %v, want %v", test.name, got, want)
    43  		}
    44  	}
    45  }
    46  
    47  func version(u uuid.UUID) byte {
    48  	return u[6] >> 4
    49  }
    50  
    51  func variant(u uuid.UUID) byte {
    52  	return u[8] >> 6
    53  }
    54  
    55  func TestNewV7Millis(t *testing.T) {
    56  	// Verify the unix_ts_ms field of a UUIDv7 is set correctly.
    57  	check := func(t *testing.T) {
    58  		t.Helper()
    59  		u := uuid.NewV7()
    60  		got := binary.BigEndian.Uint64(u[:8]) >> 16
    61  		want := uint64(time.Now().UnixMilli())
    62  		if got != want {
    63  			t.Errorf("at %v, NewV7() = %v; millis = %x, want %x", time.Now(), u, got, want)
    64  		}
    65  	}
    66  	synctest.Test(t, func(t *testing.T) {
    67  		check(t)
    68  		time.Sleep(1 * time.Hour)
    69  		check(t)
    70  		time.Sleep(time.Second - 1*time.Nanosecond) // maximum fractional seconds
    71  		check(t)
    72  		time.Sleep(2 * time.Nanosecond) // minimum fractional seconds
    73  		check(t)
    74  	})
    75  	// Testing in a new bubble causes time to go backwards.
    76  	// UUIDs should use the new time.
    77  	synctest.Test(t, func(t *testing.T) {
    78  		check(t)
    79  	})
    80  }
    81  
    82  func TestNewV7Collision(t *testing.T) {
    83  	// Verify UUIDv7s generated at the same instant do not collide and
    84  	// are monotonically increasing.
    85  	synctest.Test(t, func(t *testing.T) {
    86  		last := uuid.NewV7()
    87  		for range 3 {
    88  			// Enough iterations to overflow the fractional millisecond component
    89  			// several times.
    90  			for range (1 << 12) * 3 {
    91  				u := uuid.NewV7()
    92  				if u.Compare(last) != 1 {
    93  					t.Fatalf("NewV7 returned UUIDs out of order:\nprevious: %v\n current: %v", last, u)
    94  				}
    95  				last = u
    96  			}
    97  			// Time advances, but not as quickly as we are generating UUIDs.
    98  			time.Sleep(1 * time.Millisecond)
    99  		}
   100  	})
   101  }
   102  
   103  func TestEncode(t *testing.T) {
   104  	u := uuid.UUID{0xf8, 0x1d, 0x4f, 0xae, 0x7d, 0xec, 0x11, 0xd0, 0xa7, 0x65, 0x00, 0xa0, 0xc9, 0x1e, 0x6b, 0xf6}
   105  	want := "f81d4fae-7dec-11d0-a765-00a0c91e6bf6"
   106  	if got := u.String(); got != want {
   107  		t.Errorf("u.String() = %q, want %q", got, want)
   108  	}
   109  	if got, err := u.MarshalText(); string(got) != want || err != nil {
   110  		t.Errorf("u.MarshalText() = %q, %v; want %q, nil", got, err, want)
   111  	}
   112  	prefix := []byte("urn:uuid:")
   113  	if got, err := u.AppendText(prefix); string(got) != string(prefix)+want || err != nil {
   114  		t.Errorf("u.MarshalAppend(%q) = %q, %v; want %q, nil", prefix, got, err, string(prefix)+want)
   115  	}
   116  }
   117  
   118  func TestUnmarshalText(t *testing.T) {
   119  	var got uuid.UUID
   120  	err := got.UnmarshalText([]byte("f81d4fae-7dec-11d0-a765-00a0c91e6bf6"))
   121  	if err != nil {
   122  		t.Errorf("UnmarshalText: %v", err)
   123  	}
   124  	want := uuid.UUID{0xf8, 0x1d, 0x4f, 0xae, 0x7d, 0xec, 0x11, 0xd0, 0xa7, 0x65, 0x00, 0xa0, 0xc9, 0x1e, 0x6b, 0xf6}
   125  	if got != want {
   126  		t.Errorf("got %q, want %q", got, want)
   127  	}
   128  }
   129  
   130  func TestParseSuccess(t *testing.T) {
   131  	u1 := uuid.UUID{
   132  		0xf8, 0x1d, 0x4f, 0xae,
   133  		0x7d, 0xec,
   134  		0x11, 0xd0,
   135  		0xa7, 0x65,
   136  		0x00, 0xa0, 0xc9, 0x1e, 0x6b, 0xf6,
   137  	}
   138  	for _, test := range []struct {
   139  		s string
   140  		u uuid.UUID
   141  	}{{
   142  		s: "00000000-0000-0000-0000-000000000000",
   143  		u: uuid.Nil(),
   144  	}, {
   145  		s: "ffffffff-ffff-ffff-ffff-ffffffffffff",
   146  		u: uuid.Max(),
   147  	}, {
   148  		s: "f81d4fae-7dec-11d0-a765-00a0c91e6bf6",
   149  		u: u1,
   150  	}, {
   151  		s: "F81D4FAE-7DEC-11D0-A765-00A0C91E6BF6",
   152  		u: u1,
   153  	}, {
   154  		s: "f81d4fae7dec11d0a76500a0c91e6bf6",
   155  		u: u1,
   156  	}, {
   157  		s: "{f81d4fae-7dec-11d0-a765-00a0c91e6bf6}",
   158  		u: u1,
   159  	}, {
   160  		s: "urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6",
   161  		u: u1,
   162  	}} {
   163  		u, err := uuid.Parse(test.s)
   164  		if err != nil {
   165  			t.Errorf("Parse(%q) = _, %v; want success", test.s, err)
   166  		} else if u != test.u {
   167  			t.Errorf("Parse(%q) = %v, nil; want %v", test.s, u, test.u)
   168  		}
   169  	}
   170  }
   171  
   172  func TestParseErrors(t *testing.T) {
   173  	for _, test := range []string{
   174  		"",
   175  		"0000000000000-0000-0000-000000000000",
   176  		"00000000-000000000-0000-000000000000",
   177  		"00000000-0000-000000000-000000000000",
   178  		"00000000-0000-0000-00000000000000000",
   179  		"00000000-0000-0000-0000-00000000000",
   180  		"x0000000-0000-0000-0000-000000000000",
   181  		"00000000-x000-0000-0000-000000000000",
   182  		"00000000-0000-x000-0000-000000000000",
   183  		"00000000-0000-0000-x000-000000000000",
   184  		"00000000-0000-0000-0000-x00000000000",
   185  		"{x0000000-0000-0000-0000-000000000000}",
   186  		"urn:uuid:x000000-0000-0000-0000-000000000000",
   187  		"x0000000000000000000000000000000",
   188  		// Some parsers permit hyphens in non-standard locations,
   189  		// but we currently do not.
   190  		"0000-0000-0000-0000-0000-0000-0000-0000",
   191  		// Combinations of variant encodings that we could choose to parse,
   192  		// but currently do not.
   193  		"{00000000000000000000000000000000}",
   194  		"{urn:uuid:00000000-0000-0000-0000-000000000000}",
   195  		"urn:uuid:00000000000000000000000000000000",
   196  	} {
   197  		got, err := uuid.Parse(test)
   198  		if err == nil {
   199  			t.Errorf("Parse(%q) = %v, nil; want error", test, got)
   200  		}
   201  	}
   202  }
   203  
   204  func TestCompare(t *testing.T) {
   205  	uuids := []uuid.UUID{
   206  		uuid.Nil(),
   207  		uuid.MustParse("f81d4fae-7dec-11d0-a765-00a0c91e6bf6"),
   208  		uuid.Max(),
   209  	}
   210  	for i, u := range uuids {
   211  		if got, want := u.Compare(u), 0; got != want {
   212  			t.Errorf("%v.Compare(itself) = %v, want %v", u, got, want)
   213  		}
   214  		if i == 0 {
   215  			continue
   216  		}
   217  		prev := uuids[i-1]
   218  		if got, want := u.Compare(prev), 1; got != want {
   219  			t.Errorf("%v.Compare(%v) = %v, want %v", u, prev, got, want)
   220  		}
   221  		if got, want := prev.Compare(u), -1; got != want {
   222  			t.Errorf("%v.Compare(%v) = %v, want %v", prev, u, got, want)
   223  		}
   224  	}
   225  }
   226  
   227  func BenchmarkNewV4(b *testing.B) {
   228  	for b.Loop() {
   229  		uuid.NewV4()
   230  	}
   231  }
   232  
   233  func BenchmarkNewV7(b *testing.B) {
   234  	for b.Loop() {
   235  		uuid.NewV7()
   236  	}
   237  }
   238  
   239  func BenchmarkString(b *testing.B) {
   240  	u := uuid.MustParse("f81d4fae-7dec-11d0-a765-00a0c91e6bf6")
   241  	for b.Loop() {
   242  		_ = u.String()
   243  	}
   244  }
   245  
   246  func BenchmarkParseSuccess(b *testing.B) {
   247  	for b.Loop() {
   248  		uuid.Parse("f81d4fae-7dec-11d0-a765-00a0c91e6bf6")
   249  	}
   250  }
   251  
   252  func BenchmarkParseError(b *testing.B) {
   253  	for b.Loop() {
   254  		uuid.Parse("00000000-0000-0000-0000-00000000000X")
   255  	}
   256  }
   257  

View as plain text