1package tuple 2 3import ( 4 "bytes" 5 "encoding/gob" 6 "flag" 7 "math/rand" 8 "os" 9 "testing" 10) 11 12var update = flag.Bool("update", false, "update .golden files") 13 14func loadGolden(t *testing.T) (golden map[string][]byte) { 15 f, err := os.Open("testdata/tuples.golden") 16 if err != nil { 17 t.Fatalf("failed to open golden file: %s", err) 18 } 19 defer f.Close() 20 21 err = gob.NewDecoder(f).Decode(&golden) 22 if err != nil { 23 t.Fatalf("failed to decode golden file: %s", err) 24 } 25 return 26} 27 28func writeGolden(t *testing.T, golden map[string][]byte) { 29 f, err := os.Create("testdata/tuples.golden") 30 if err != nil { 31 t.Fatalf("failed to open golden file: %s", err) 32 } 33 defer f.Close() 34 35 err = gob.NewEncoder(f).Encode(golden) 36 if err != nil { 37 t.Fatalf("failed to encode golden file: %s", err) 38 } 39} 40 41var testUUID = UUID{ 42 0x11, 0x00, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 43 0x11, 0x00, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 44} 45 46func genBytes() interface{} { return []byte("namespace") } 47func genBytesNil() interface{} { return []byte{0xFF, 0x00, 0xFF} } 48func genString() interface{} { return "namespace" } 49func genStringNil() interface{} { return "nam\x00es\xFFpace" } 50func genInt() interface{} { return rand.Int63() } 51func genFloat() interface{} { return float32(rand.NormFloat64()) } 52func genDouble() interface{} { return rand.NormFloat64() } 53 54func mktuple(gen func() interface{}, count int) Tuple { 55 tt := make(Tuple, count) 56 for i := 0; i < count; i++ { 57 tt[i] = gen() 58 } 59 return tt 60} 61 62var testCases = []struct { 63 name string 64 tuple Tuple 65}{ 66 {"Simple", Tuple{testUUID, "foobarbaz", 1234, nil}}, 67 {"Namespaces", Tuple{testUUID, "github", "com", "apple", "foundationdb", "tree"}}, 68 {"ManyStrings", mktuple(genString, 8)}, 69 {"ManyStringsNil", mktuple(genStringNil, 8)}, 70 {"ManyBytes", mktuple(genBytes, 20)}, 71 {"ManyBytesNil", mktuple(genBytesNil, 20)}, 72 {"LargeBytes", Tuple{testUUID, bytes.Repeat([]byte("abcd"), 20)}}, 73 {"LargeBytesNil", Tuple{testUUID, bytes.Repeat([]byte{0xFF, 0x0, 0xFF}, 20)}}, 74 {"Integers", mktuple(genInt, 20)}, 75 {"Floats", mktuple(genFloat, 20)}, 76 {"Doubles", mktuple(genDouble, 20)}, 77 {"UUIDs", Tuple{testUUID, true, testUUID, false, testUUID, true, testUUID, false, testUUID, true}}, 78 {"NilCases", Tuple{"\x00", "\x00\xFF", "\x00\x00\x00", "\xFF\x00", ""}}, 79 {"Nested", Tuple{testUUID, mktuple(genInt, 4), nil, mktuple(genBytes, 4), nil, mktuple(genDouble, 4), nil}}, 80} 81 82func TestTuplePacking(t *testing.T) { 83 var golden map[string][]byte 84 85 if *update { 86 golden = make(map[string][]byte) 87 } else { 88 golden = loadGolden(t) 89 } 90 91 for _, tt := range testCases { 92 t.Run(tt.name, func(t *testing.T) { 93 result := tt.tuple.Pack() 94 95 if *update { 96 golden[tt.name] = result 97 return 98 } 99 100 if !bytes.Equal(result, golden[tt.name]) { 101 t.Errorf("packing mismatch: expected %v, got %v", golden[tt.name], result) 102 } 103 }) 104 } 105 106 if *update { 107 writeGolden(t, golden) 108 } 109} 110 111func BenchmarkTuplePacking(b *testing.B) { 112 for _, bm := range testCases { 113 b.Run(bm.name, func(b *testing.B) { 114 tuple := bm.tuple 115 for i := 0; i < b.N; i++ { 116 _ = tuple.Pack() 117 } 118 }) 119 } 120} 121