1// Copyright 2018 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package protoreflect 6 7import ( 8 "bytes" 9 "math" 10 "reflect" 11 "testing" 12) 13 14func TestValue(t *testing.T) { 15 fakeMessage := new(struct{ Message }) 16 fakeList := new(struct{ List }) 17 fakeMap := new(struct{ Map }) 18 19 tests := []struct { 20 in Value 21 want interface{} 22 }{ 23 {in: Value{}}, 24 {in: ValueOf(nil)}, 25 {in: ValueOf(true), want: true}, 26 {in: ValueOf(int32(math.MaxInt32)), want: int32(math.MaxInt32)}, 27 {in: ValueOf(int64(math.MaxInt64)), want: int64(math.MaxInt64)}, 28 {in: ValueOf(uint32(math.MaxUint32)), want: uint32(math.MaxUint32)}, 29 {in: ValueOf(uint64(math.MaxUint64)), want: uint64(math.MaxUint64)}, 30 {in: ValueOf(float32(math.MaxFloat32)), want: float32(math.MaxFloat32)}, 31 {in: ValueOf(float64(math.MaxFloat64)), want: float64(math.MaxFloat64)}, 32 {in: ValueOf(string("hello")), want: string("hello")}, 33 {in: ValueOf([]byte("hello")), want: []byte("hello")}, 34 {in: ValueOf(fakeMessage), want: fakeMessage}, 35 {in: ValueOf(fakeList), want: fakeList}, 36 {in: ValueOf(fakeMap), want: fakeMap}, 37 } 38 39 for _, tt := range tests { 40 got := tt.in.Interface() 41 if !reflect.DeepEqual(got, tt.want) { 42 t.Errorf("Value(%v).Interface() = %v, want %v", tt.in, got, tt.want) 43 } 44 45 if got := tt.in.IsValid(); got != (tt.want != nil) { 46 t.Errorf("Value(%v).IsValid() = %v, want %v", tt.in, got, tt.want != nil) 47 } 48 switch want := tt.want.(type) { 49 case int32: 50 if got := tt.in.Int(); got != int64(want) { 51 t.Errorf("Value(%v).Int() = %v, want %v", tt.in, got, tt.want) 52 } 53 case int64: 54 if got := tt.in.Int(); got != int64(want) { 55 t.Errorf("Value(%v).Int() = %v, want %v", tt.in, got, tt.want) 56 } 57 case uint32: 58 if got := tt.in.Uint(); got != uint64(want) { 59 t.Errorf("Value(%v).Uint() = %v, want %v", tt.in, got, tt.want) 60 } 61 case uint64: 62 if got := tt.in.Uint(); got != uint64(want) { 63 t.Errorf("Value(%v).Uint() = %v, want %v", tt.in, got, tt.want) 64 } 65 case float32: 66 if got := tt.in.Float(); got != float64(want) { 67 t.Errorf("Value(%v).Float() = %v, want %v", tt.in, got, tt.want) 68 } 69 case float64: 70 if got := tt.in.Float(); got != float64(want) { 71 t.Errorf("Value(%v).Float() = %v, want %v", tt.in, got, tt.want) 72 } 73 case string: 74 if got := tt.in.String(); got != string(want) { 75 t.Errorf("Value(%v).String() = %v, want %v", tt.in, got, tt.want) 76 } 77 case []byte: 78 if got := tt.in.Bytes(); !bytes.Equal(got, want) { 79 t.Errorf("Value(%v).Bytes() = %v, want %v", tt.in, got, tt.want) 80 } 81 case EnumNumber: 82 if got := tt.in.Enum(); got != want { 83 t.Errorf("Value(%v).Enum() = %v, want %v", tt.in, got, tt.want) 84 } 85 case Message: 86 if got := tt.in.Message(); got != want { 87 t.Errorf("Value(%v).Message() = %v, want %v", tt.in, got, tt.want) 88 } 89 case List: 90 if got := tt.in.List(); got != want { 91 t.Errorf("Value(%v).List() = %v, want %v", tt.in, got, tt.want) 92 } 93 case Map: 94 if got := tt.in.Map(); got != want { 95 t.Errorf("Value(%v).Map() = %v, want %v", tt.in, got, tt.want) 96 } 97 } 98 } 99} 100 101func BenchmarkValue(b *testing.B) { 102 const testdata = "The quick brown fox jumped over the lazy dog." 103 var sink1 string 104 var sink2 Value 105 var sink3 interface{} 106 107 // Baseline measures the time to store a string into a native variable. 108 b.Run("Baseline", func(b *testing.B) { 109 b.ReportAllocs() 110 for i := 0; i < b.N; i++ { 111 sink1 = testdata[:len(testdata)%(i+1)] 112 } 113 }) 114 115 // Inline measures the time to store a string into a Value, 116 // assuming that the compiler could inline the ValueOf function call. 117 b.Run("Inline", func(b *testing.B) { 118 b.ReportAllocs() 119 for i := 0; i < b.N; i++ { 120 sink2 = valueOfString(testdata[:len(testdata)%(i+1)]) 121 } 122 }) 123 124 // Value measures the time to store a string into a Value using the general 125 // ValueOf function call. This should be identical to Inline. 126 // 127 // NOTE: As of Go1.11, this is not as efficient as Inline due to the lack 128 // of some compiler optimizations: 129 // https://golang.org/issue/22310 130 // https://golang.org/issue/25189 131 b.Run("Value", func(b *testing.B) { 132 b.ReportAllocs() 133 for i := 0; i < b.N; i++ { 134 sink2 = ValueOf(string(testdata[:len(testdata)%(i+1)])) 135 } 136 }) 137 138 // Interface measures the time to store a string into an interface. 139 b.Run("Interface", func(b *testing.B) { 140 b.ReportAllocs() 141 for i := 0; i < b.N; i++ { 142 sink3 = string(testdata[:len(testdata)%(i+1)]) 143 } 144 }) 145 146 _, _, _ = sink1, sink2, sink3 147} 148