package uuids_test import ( "encoding/binary" "testing" "pkg.jfrech.com/brief/internal/crumbs" "pkg.jfrech.com/brief/uuids" ) func TestParse(t *testing.T) { type Testcase struct { Text string Parses bool IsRFC4122v4 bool } tcs := []Testcase{ Testcase{uuids.UUID([16]byte{0,0,0,0, 0,0, 0b0100_0000,0, 0b10_000000, 0, 0,0,0,0,0,0}).String(), true, true}, Testcase{"00000000-0000-b000-4000-000000000000", true, false}, Testcase{"27c1344f-b15f-d2ce-d44b-63ed65ceca0e", true, false}, Testcase{"aeaaf34a-b67a-43fd-7081-e8d4a9cfc7c2", true, false}, Testcase{"cad92b45-b9bb-c642-0904-aa349435b97f", true, false}, Testcase{"2e023c40-abc9-fc90-a853-d2ecdd031808", true, false}, Testcase{"c83bb24b-a94d-fa27-f8cb-28624d3ceca0", true, false}, Testcase{"b763ac4a-b5ec-7920-9adc-0ad5367636bd", true, false}, Testcase{"19d30243-a4ba-df5f-69d5-93c95226bfc0", true, false}, Testcase{"6dc1d044-9ab9-5c08-06ef-534c4c241921", true, false}, Testcase{"05d09147-9fb8-90b7-6cba-4ede04320243", true, false}, Testcase{"b97a8c42-8006-b3e0-de99-1c267a380048", true, false}, Testcase{"faf2a448-96ec-4144-4016-32f232ae9dc0", true, false}, Testcase{"db202c44-ae28-78fd-9e17-410648ba8f5d", true, false}, Testcase{"4c0d8746-a1dd-b098-0934-2a4ff5335a9a", true, false}, Testcase{"7053d74f-8adc-974c-6193-e66ed59c90e5", true, false}, Testcase{"5fc62149-8deb-b1a3-ec02-dd9fce0c8720", true, false}, Testcase{"42ca8341-af0c-0bce-b53c-2f018f262ed8", true, false}, Testcase{"dec3fc40-870c-9c83-91e5-02b77e267a6e", true, false}, Testcase{"9f74c248-90f7-4134-6983-8c8fd9fd940b", true, false}, Testcase{"f860354a-8e7d-e9d4-7f0a-4bed710aab30", true, false}, Testcase{"f730bd4b-981b-091e-fab3-6ec4806e27ef", true, false}, Testcase{"4bb0b547-9682-8658-f3a1-4ffdc1b3028e", true, false}, Testcase{"8178d643-a481-285e-61b5-c982269723d4", true, false}, Testcase{"ba395440-abf8-c86e-b6af-ac515a38824d", true, false}, Testcase{"45a1704d-a869-5a73-5a18-c1ca79d02b6d", true, false}, Testcase{"6e70984d-897e-5696-9fae-fc8eab8c8e4d", true, false}, Testcase{"407ea845-9234-faf8-4a43-96ae98239b6d", true, false}, Testcase{"cbd3d74a-9387-f673-d1c6-ca9cb94732f9", true, false}, } for _, tc := range tcs { uuid, err := crumbs.ParseStringDereference[uuids.UUID](tc.Text) if !tc.Parses { if err == nil { t.Errorf("inventive parse: %q", tc.Text) } continue } if err != nil { t.Errorf("no parse: %q", tc.Text) continue } if !uuid.IsRFC4122v4() && tc.IsRFC4122v4 { t.Errorf("not RFC4122v4: %q", tc.Text) } else if uuid.IsRFC4122v4() && !tc.IsRFC4122v4 { t.Errorf("inventively RFC4122v4: %q", tc.Text) } } } func TestParseNeg(t *testing.T) { for _, s := range []string{ "", "32e018fe-aee2-4042-8010-cd52dfcf9a3", "32e018feaee2-4042-8010-cd52dfcf9a3", "32e018feaee240428010cd52dfcf9a3", "32e018fe-aee2_4042-8010-cd52dfcf9a3e", "32e018fe aee2 4042 8010 cd52dfcf9a3e", "32e018fe-aee2-4042-80z0-cd52dfcf9a3e", } { uuid, err := crumbs.ParseStringDereference[uuids.UUID](s) if err == nil { t.Errorf("inventive: %q -> %s", s, uuid) } } } func assertRFC4122v4(t *testing.T, uuid uuids.UUID) { t.Helper() okVariant, okVersion := isRFC4122v4(uuid) if !okVariant { t.Errorf("UUID of non-RFC4122 variant: %s", uuid) } if !okVersion { t.Errorf("UUID of non-v4 version: %s", uuid) } } func isRFC4122v4(uuid uuids.UUID) (okVariant, okVersion bool) { uuid_t := struct { time_low uint32 time_mid uint16 time_hi_and_version uint16 clock_seq_hi_and_reserved uint8 clock_seq_low uint8 node [6]byte }{ binary.BigEndian.Uint32(uuid[0:][:4]), binary.BigEndian.Uint16(uuid[4:][:2]), binary.BigEndian.Uint16(uuid[6:][:2]), byte(uuid[8:][:1][0]), byte(uuid[9:][:1][0]), [6]byte(uuid[10:][:6]), } okVariant = true // Bits {6,7} should be {0,1}. // // Cf. https://www.rfc-editor.org/rfc/rfc4122.html#section-4.4, first bullet if uuid_t.clock_seq_hi_and_reserved & (1 << 6) != 0 { okVariant = false } else if uuid_t.clock_seq_hi_and_reserved & (1 << 7) == 0 { okVariant = false } okVersion = true // Bits {15,14,13,12} should be {Msb0,Msb1,Msb2,Msb3} should be {0,1,0,0}, // thus bits {12,13,14,15} should be {0,0,1,0}. // // Cf. https://www.rfc-editor.org/rfc/rfc4122.html#section-4.4, second bullet // Cf. https://www.rfc-editor.org/rfc/rfc4122.html#section-4.1.3 if uuid_t.time_hi_and_version & (1 << 12) != 0 { okVersion = false } else if uuid_t.time_hi_and_version & (1 << 13) != 0 { okVersion = false } else if uuid_t.time_hi_and_version & (1 << 14) == 0 { okVersion = false } else if uuid_t.time_hi_and_version & (1 << 15) != 0 { okVersion = false } return } func FuzzIsRFC4122v4(f *testing.F) { f.Fuzz(func(t *testing.T, uuid_ []byte) { if len(uuid_) != 16 { t.SkipNow() } uuid := uuids.UUID(uuid_) if testing.Verbose() { t.Log(uuid) } okVariant, okVersion := isRFC4122v4(uuid) ok := uuid.IsRFC4122v4() if okVariant && okVersion != ok { t.Errorf("okVariant=%v, okVersion=%v, ok=%v: %s", okVariant, okVersion, ok, uuid) } }) } // a string which parses must marshal to a parsable string of same uuid func FuzzParse(f *testing.F) { f.Fuzz(func(t *testing.T, s string) { uuid, err := crumbs.ParseStringDereference[uuids.UUID](s) if err != nil { t.SkipNow() } s2 := uuid.String() uuid2, err := crumbs.ParseStringDereference[uuids.UUID](s2) if err != nil { t.Errorf("degraded: %q -> %s -> %q -> %v", s, uuid, s2, err) } if uuid != uuid2 { t.Errorf("degraded: %q -> %s -> %q -> %s", s, uuid, s2, uuid2) } }) } func FuzzV4(f *testing.F) { for dummy := range 32 { f.Add(dummy) } f.Fuzz(func(t *testing.T, _ int) { uuid := uuids.V4() if testing.Verbose() { t.Log(uuid) } if uuid.IsZero() { t.Fatalf("generated zero UUID: %s", uuid) } assertRFC4122v4(t, uuid) }) } func TestMarshal(t *testing.T) { if true { var uuid uuids.UUID s, err := uuid.MarshalText() if err != nil { t.Fatal(err) } if string(s) != "00000000-0000-0000-0000-000000000000" { t.Errorf("odd empty uuid string: %q", s) } } if true { var uuid uuids.UUID s := "32e018fe-aee2-4042-8010-cd52dfcf9a3e" err := (&uuid).UnmarshalText([]byte(s)) if err != nil { t.Fatal(err) } z, err := uuid.MarshalText() if err != nil { t.Fatal(err) } if s != string(z) { t.Fatalf("s=%q, z=%q", s, z) } } }