// 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" "flag" "fmt" "io/ioutil" "os" "path/filepath" "testing" ) // codecTestData holds []byte data for a UUID we commonly use for testing. var codecTestData = []byte{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} // codecTestUUID is the UUID value corresponding to codecTestData. var codecTestUUID = UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} func TestFromBytes(t *testing.T) { t.Run("Valid", func(t *testing.T) { got, err := FromBytes(codecTestData) if err != nil { t.Fatal(err) } if got != codecTestUUID { t.Fatalf("FromBytes(%x) = %v, want %v", codecTestData, got, codecTestUUID) } }) t.Run("Invalid", func(t *testing.T) { var short [][]byte for i := 0; i < len(codecTestData); i++ { short = append(short, codecTestData[:i]) } var long [][]byte for i := 1; i < 17; i++ { tmp := append(codecTestData, make([]byte, i)...) long = append(long, tmp) } invalid := append(short, long...) for _, b := range invalid { got, err := FromBytes(b) if err == nil { t.Fatalf("FromBytes(%x): want err != nil, got %v", b, got) } } }) } func TestFromBytesOrNil(t *testing.T) { t.Run("Invalid", func(t *testing.T) { b := []byte{4, 8, 15, 16, 23, 42} got := FromBytesOrNil(b) if got != Nil { t.Errorf("FromBytesOrNil(%x): got %v, want %v", b, got, Nil) } }) t.Run("Valid", func(t *testing.T) { got := FromBytesOrNil(codecTestData) if got != codecTestUUID { t.Errorf("FromBytesOrNil(%x): got %v, want %v", codecTestData, got, codecTestUUID) } }) } type fromStringTest struct { input string variant string } // Run runs the FromString test in a subtest of t, named by fst.variant. func (fst fromStringTest) Run(t *testing.T) { t.Run(fst.variant, func(t *testing.T) { got, err := FromString(fst.input) if err != nil { t.Fatalf("FromString(%q): %v", fst.input, err) } if want := codecTestUUID; got != want { t.Fatalf("FromString(%q) = %v, want %v", fst.input, got, want) } }) } // fromStringTests contains UUID variants that are expected to be parsed // successfully by UnmarshalText / FromString. // // variants must be unique across elements of this slice. Please see the // comment in fuzz.go if you change this slice or add new tests to it. var fromStringTests = []fromStringTest{ { input: "6ba7b810-9dad-11d1-80b4-00c04fd430c8", variant: "Canonical", }, { input: "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}", variant: "BracedCanonical", }, { input: "{6ba7b8109dad11d180b400c04fd430c8}", variant: "BracedHashlike", }, { input: "6ba7b8109dad11d180b400c04fd430c8", variant: "Hashlike", }, { input: "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8", variant: "URNCanonical", }, { input: "urn:uuid:6ba7b8109dad11d180b400c04fd430c8", variant: "URNHashlike", }, } var invalidFromStringInputs = []string{ // short "6ba7b810-9dad-11d1-80b4-00c04fd430c", "6ba7b8109dad11d180b400c04fd430c", // invalid hex "6ba7b8109dad11d180b400c04fd430q8", // long "6ba7b810-9dad-11d1-80b4-00c04fd430c8=", "6ba7b810-9dad-11d1-80b4-00c04fd430c8}", "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}f", "6ba7b810-9dad-11d1-80b4-00c04fd430c800c04fd430c8", // malformed in other ways "ba7b8109dad11d180b400c04fd430c8}", "6ba7b8109dad11d180b400c04fd430c86ba7b8109dad11d180b400c04fd430c8", "urn:uuid:{6ba7b810-9dad-11d1-80b4-00c04fd430c8}", "uuid:urn:6ba7b810-9dad-11d1-80b4-00c04fd430c8", "uuid:urn:6ba7b8109dad11d180b400c04fd430c8", "6ba7b8109-dad-11d1-80b4-00c04fd430c8", "6ba7b810-9dad1-1d1-80b4-00c04fd430c8", "6ba7b810-9dad-11d18-0b4-00c04fd430c8", "6ba7b810-9dad-11d1-80b40-0c04fd430c8", "6ba7b810+9dad+11d1+80b4+00c04fd430c8", "(6ba7b810-9dad-11d1-80b4-00c04fd430c8}", "{6ba7b810-9dad-11d1-80b4-00c04fd430c8>", "zba7b810-9dad-11d1-80b4-00c04fd430c8", "6ba7b810-9dad11d180b400c04fd430c8", "6ba7b8109dad-11d180b400c04fd430c8", "6ba7b8109dad11d1-80b400c04fd430c8", "6ba7b8109dad11d180b4-00c04fd430c8", } func TestFromString(t *testing.T) { t.Run("Valid", func(t *testing.T) { for _, fst := range fromStringTests { fst.Run(t) } }) t.Run("Invalid", func(t *testing.T) { for _, s := range invalidFromStringInputs { got, err := FromString(s) if err == nil { t.Errorf("FromString(%q): want err != nil, got %v", s, got) } } }) } func TestFromStringOrNil(t *testing.T) { t.Run("Invalid", func(t *testing.T) { s := "bad" got := FromStringOrNil(s) if got != Nil { t.Errorf("FromStringOrNil(%q): got %v, want Nil", s, got) } }) t.Run("Valid", func(t *testing.T) { s := "6ba7b810-9dad-11d1-80b4-00c04fd430c8" got := FromStringOrNil(s) if got != codecTestUUID { t.Errorf("FromStringOrNil(%q): got %v, want %v", s, got, codecTestUUID) } }) } func TestMarshalBinary(t *testing.T) { got, err := codecTestUUID.MarshalBinary() if err != nil { t.Fatal(err) } if !bytes.Equal(got, codecTestData) { t.Fatalf("%v.MarshalBinary() = %x, want %x", codecTestUUID, got, codecTestData) } } func TestMarshalText(t *testing.T) { want := []byte("6ba7b810-9dad-11d1-80b4-00c04fd430c8") got, err := codecTestUUID.MarshalText() if err != nil { t.Fatal(err) } if !bytes.Equal(got, want) { t.Errorf("%v.MarshalText(): got %s, want %s", codecTestUUID, got, want) } } func TestDecodePlainWithWrongLength(t *testing.T) { arg := []byte{'4', '2'} u := UUID{} if u.decodePlain(arg) == nil { t.Errorf("%v.decodePlain(%q): should return error, but it did not", u, arg) } } var stringBenchmarkSink string func BenchmarkString(b *testing.B) { for i := 0; i < b.N; i++ { stringBenchmarkSink = codecTestUUID.String() } } func BenchmarkFromBytes(b *testing.B) { for i := 0; i < b.N; i++ { FromBytes(codecTestData) } } func BenchmarkFromString(b *testing.B) { b.Run("canonical", func(b *testing.B) { for i := 0; i < b.N; i++ { FromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8") } }) b.Run("urn", func(b *testing.B) { for i := 0; i < b.N; i++ { FromString("urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8") } }) b.Run("braced", func(b *testing.B) { for i := 0; i < b.N; i++ { FromString("{6ba7b810-9dad-11d1-80b4-00c04fd430c8}") } }) } func BenchmarkMarshalBinary(b *testing.B) { for i := 0; i < b.N; i++ { codecTestUUID.MarshalBinary() } } func BenchmarkMarshalText(b *testing.B) { for i := 0; i < b.N; i++ { codecTestUUID.MarshalText() } } var seedFuzzCorpus = flag.Bool("seed_fuzz_corpus", false, "seed fuzz test corpus") func TestSeedFuzzCorpus(t *testing.T) { // flag.Parse() is called for us by the test binary. if !*seedFuzzCorpus { t.Skip("seeding fuzz test corpus only on demand") } corpusDir := filepath.Join(".", "testdata", "corpus") writeSeedFile := func(name, data string) error { path := filepath.Join(corpusDir, name) return ioutil.WriteFile(path, []byte(data), os.ModePerm) } for _, fst := range fromStringTests { name := "seed_valid_" + fst.variant if err := writeSeedFile(name, fst.input); err != nil { t.Fatal(err) } } for i, s := range invalidFromStringInputs { name := fmt.Sprintf("seed_invalid_%d", i) if err := writeSeedFile(name, s); err != nil { t.Fatal(err) } } }