github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/util/uuid/codec_test.go (about) 1 // Copyright 2019 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 // Copyright (C) 2013-2018 by Maxim Bublis <b@codemonkey.ru> 12 // Use of this source code is governed by a MIT-style 13 // license that can be found in licenses/MIT-gofrs.txt. 14 15 // This code originated in github.com/gofrs/uuid. 16 17 package uuid 18 19 import ( 20 "bytes" 21 "flag" 22 "fmt" 23 "io/ioutil" 24 "os" 25 "path/filepath" 26 "testing" 27 ) 28 29 // codecTestData holds []byte data for a UUID we commonly use for testing. 30 var codecTestData = []byte{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} 31 32 // codecTestUUID is the UUID value corresponding to codecTestData. 33 var codecTestUUID = UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} 34 35 func TestFromBytes(t *testing.T) { 36 t.Run("Valid", func(t *testing.T) { 37 got, err := FromBytes(codecTestData) 38 if err != nil { 39 t.Fatal(err) 40 } 41 if got != codecTestUUID { 42 t.Fatalf("FromBytes(%x) = %v, want %v", codecTestData, got, codecTestUUID) 43 } 44 }) 45 t.Run("Invalid", func(t *testing.T) { 46 var short [][]byte 47 for i := 0; i < len(codecTestData); i++ { 48 short = append(short, codecTestData[:i]) 49 } 50 var long [][]byte 51 for i := 1; i < 17; i++ { 52 tmp := append(codecTestData, make([]byte, i)...) 53 long = append(long, tmp) 54 } 55 invalid := append(short, long...) 56 for _, b := range invalid { 57 got, err := FromBytes(b) 58 if err == nil { 59 t.Fatalf("FromBytes(%x): want err != nil, got %v", b, got) 60 } 61 } 62 }) 63 } 64 65 func TestFromBytesOrNil(t *testing.T) { 66 t.Run("Invalid", func(t *testing.T) { 67 b := []byte{4, 8, 15, 16, 23, 42} 68 got := FromBytesOrNil(b) 69 if got != Nil { 70 t.Errorf("FromBytesOrNil(%x): got %v, want %v", b, got, Nil) 71 } 72 }) 73 t.Run("Valid", func(t *testing.T) { 74 got := FromBytesOrNil(codecTestData) 75 if got != codecTestUUID { 76 t.Errorf("FromBytesOrNil(%x): got %v, want %v", codecTestData, got, codecTestUUID) 77 } 78 }) 79 80 } 81 82 type fromStringTest struct { 83 input string 84 variant string 85 } 86 87 // Run runs the FromString test in a subtest of t, named by fst.variant. 88 func (fst fromStringTest) Run(t *testing.T) { 89 t.Run(fst.variant, func(t *testing.T) { 90 got, err := FromString(fst.input) 91 if err != nil { 92 t.Fatalf("FromString(%q): %v", fst.input, err) 93 } 94 if want := codecTestUUID; got != want { 95 t.Fatalf("FromString(%q) = %v, want %v", fst.input, got, want) 96 } 97 }) 98 } 99 100 // fromStringTests contains UUID variants that are expected to be parsed 101 // successfully by UnmarshalText / FromString. 102 // 103 // variants must be unique across elements of this slice. Please see the 104 // comment in fuzz.go if you change this slice or add new tests to it. 105 var fromStringTests = []fromStringTest{ 106 { 107 input: "6ba7b810-9dad-11d1-80b4-00c04fd430c8", 108 variant: "Canonical", 109 }, 110 { 111 input: "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}", 112 variant: "BracedCanonical", 113 }, 114 { 115 input: "{6ba7b8109dad11d180b400c04fd430c8}", 116 variant: "BracedHashlike", 117 }, 118 { 119 input: "6ba7b8109dad11d180b400c04fd430c8", 120 variant: "Hashlike", 121 }, 122 { 123 input: "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8", 124 variant: "URNCanonical", 125 }, 126 { 127 input: "urn:uuid:6ba7b8109dad11d180b400c04fd430c8", 128 variant: "URNHashlike", 129 }, 130 } 131 132 var invalidFromStringInputs = []string{ 133 // short 134 "6ba7b810-9dad-11d1-80b4-00c04fd430c", 135 "6ba7b8109dad11d180b400c04fd430c", 136 137 // invalid hex 138 "6ba7b8109dad11d180b400c04fd430q8", 139 140 // long 141 "6ba7b810-9dad-11d1-80b4-00c04fd430c8=", 142 "6ba7b810-9dad-11d1-80b4-00c04fd430c8}", 143 "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}f", 144 "6ba7b810-9dad-11d1-80b4-00c04fd430c800c04fd430c8", 145 146 // malformed in other ways 147 "ba7b8109dad11d180b400c04fd430c8}", 148 "6ba7b8109dad11d180b400c04fd430c86ba7b8109dad11d180b400c04fd430c8", 149 "urn:uuid:{6ba7b810-9dad-11d1-80b4-00c04fd430c8}", 150 "uuid:urn:6ba7b810-9dad-11d1-80b4-00c04fd430c8", 151 "uuid:urn:6ba7b8109dad11d180b400c04fd430c8", 152 "6ba7b8109-dad-11d1-80b4-00c04fd430c8", 153 "6ba7b810-9dad1-1d1-80b4-00c04fd430c8", 154 "6ba7b810-9dad-11d18-0b4-00c04fd430c8", 155 "6ba7b810-9dad-11d1-80b40-0c04fd430c8", 156 "6ba7b810+9dad+11d1+80b4+00c04fd430c8", 157 "(6ba7b810-9dad-11d1-80b4-00c04fd430c8}", 158 "{6ba7b810-9dad-11d1-80b4-00c04fd430c8>", 159 "zba7b810-9dad-11d1-80b4-00c04fd430c8", 160 "6ba7b810-9dad11d180b400c04fd430c8", 161 "6ba7b8109dad-11d180b400c04fd430c8", 162 "6ba7b8109dad11d1-80b400c04fd430c8", 163 "6ba7b8109dad11d180b4-00c04fd430c8", 164 } 165 166 func TestFromString(t *testing.T) { 167 t.Run("Valid", func(t *testing.T) { 168 for _, fst := range fromStringTests { 169 fst.Run(t) 170 } 171 }) 172 t.Run("Invalid", func(t *testing.T) { 173 for _, s := range invalidFromStringInputs { 174 got, err := FromString(s) 175 if err == nil { 176 t.Errorf("FromString(%q): want err != nil, got %v", s, got) 177 } 178 } 179 }) 180 } 181 182 func TestFromStringOrNil(t *testing.T) { 183 t.Run("Invalid", func(t *testing.T) { 184 s := "bad" 185 got := FromStringOrNil(s) 186 if got != Nil { 187 t.Errorf("FromStringOrNil(%q): got %v, want Nil", s, got) 188 } 189 }) 190 t.Run("Valid", func(t *testing.T) { 191 s := "6ba7b810-9dad-11d1-80b4-00c04fd430c8" 192 got := FromStringOrNil(s) 193 if got != codecTestUUID { 194 t.Errorf("FromStringOrNil(%q): got %v, want %v", s, got, codecTestUUID) 195 } 196 }) 197 } 198 199 func TestMarshalBinary(t *testing.T) { 200 got, err := codecTestUUID.MarshalBinary() 201 if err != nil { 202 t.Fatal(err) 203 } 204 if !bytes.Equal(got, codecTestData) { 205 t.Fatalf("%v.MarshalBinary() = %x, want %x", codecTestUUID, got, codecTestData) 206 } 207 } 208 209 func TestMarshalText(t *testing.T) { 210 want := []byte("6ba7b810-9dad-11d1-80b4-00c04fd430c8") 211 got, err := codecTestUUID.MarshalText() 212 if err != nil { 213 t.Fatal(err) 214 } 215 if !bytes.Equal(got, want) { 216 t.Errorf("%v.MarshalText(): got %s, want %s", codecTestUUID, got, want) 217 } 218 } 219 220 func TestDecodePlainWithWrongLength(t *testing.T) { 221 arg := []byte{'4', '2'} 222 223 u := UUID{} 224 225 if u.decodePlain(arg) == nil { 226 t.Errorf("%v.decodePlain(%q): should return error, but it did not", u, arg) 227 } 228 } 229 230 var stringBenchmarkSink string 231 232 func BenchmarkString(b *testing.B) { 233 for i := 0; i < b.N; i++ { 234 stringBenchmarkSink = codecTestUUID.String() 235 } 236 } 237 238 func BenchmarkFromBytes(b *testing.B) { 239 for i := 0; i < b.N; i++ { 240 Must(FromBytes(codecTestData)) 241 } 242 } 243 244 func BenchmarkFromString(b *testing.B) { 245 b.Run("canonical", func(b *testing.B) { 246 for i := 0; i < b.N; i++ { 247 Must(FromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8")) 248 } 249 }) 250 b.Run("urn", func(b *testing.B) { 251 for i := 0; i < b.N; i++ { 252 Must(FromString("urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8")) 253 } 254 }) 255 b.Run("braced", func(b *testing.B) { 256 for i := 0; i < b.N; i++ { 257 Must(FromString("{6ba7b810-9dad-11d1-80b4-00c04fd430c8}")) 258 } 259 }) 260 } 261 262 func BenchmarkMarshalBinary(b *testing.B) { 263 for i := 0; i < b.N; i++ { 264 if _, err := codecTestUUID.MarshalBinary(); err != nil { 265 panic(err) 266 } 267 } 268 } 269 270 func BenchmarkMarshalText(b *testing.B) { 271 for i := 0; i < b.N; i++ { 272 if _, err := codecTestUUID.MarshalText(); err != nil { 273 panic(err) 274 } 275 } 276 } 277 278 var seedFuzzCorpus = flag.Bool("seed_fuzz_corpus", false, "seed fuzz test corpus") 279 280 func TestSeedFuzzCorpus(t *testing.T) { 281 // flag.Parse() is called for us by the test binary. 282 if !*seedFuzzCorpus { 283 t.Skip("seeding fuzz test corpus only on demand") 284 } 285 corpusDir := filepath.Join(".", "testdata", "corpus") 286 writeSeedFile := func(name, data string) error { 287 path := filepath.Join(corpusDir, name) 288 return ioutil.WriteFile(path, []byte(data), os.ModePerm) 289 } 290 for _, fst := range fromStringTests { 291 name := "seed_valid_" + fst.variant 292 if err := writeSeedFile(name, fst.input); err != nil { 293 t.Fatal(err) 294 } 295 } 296 for i, s := range invalidFromStringInputs { 297 name := fmt.Sprintf("seed_invalid_%d", i) 298 if err := writeSeedFile(name, s); err != nil { 299 t.Fatal(err) 300 } 301 } 302 }