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  }