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 14var ( 15 fakeMessage = new(struct{ Message }) 16 fakeList = new(struct{ List }) 17 fakeMap = new(struct{ Map }) 18) 19 20func TestValue(t *testing.T) { 21 22 tests := []struct { 23 in Value 24 want interface{} 25 }{ 26 {in: Value{}}, 27 {in: ValueOf(nil)}, 28 {in: ValueOf(true), want: true}, 29 {in: ValueOf(int32(math.MaxInt32)), want: int32(math.MaxInt32)}, 30 {in: ValueOf(int64(math.MaxInt64)), want: int64(math.MaxInt64)}, 31 {in: ValueOf(uint32(math.MaxUint32)), want: uint32(math.MaxUint32)}, 32 {in: ValueOf(uint64(math.MaxUint64)), want: uint64(math.MaxUint64)}, 33 {in: ValueOf(float32(math.MaxFloat32)), want: float32(math.MaxFloat32)}, 34 {in: ValueOf(float64(math.MaxFloat64)), want: float64(math.MaxFloat64)}, 35 {in: ValueOf(string("hello")), want: string("hello")}, 36 {in: ValueOf([]byte("hello")), want: []byte("hello")}, 37 {in: ValueOf(fakeMessage), want: fakeMessage}, 38 {in: ValueOf(fakeList), want: fakeList}, 39 {in: ValueOf(fakeMap), want: fakeMap}, 40 } 41 42 for _, tt := range tests { 43 got := tt.in.Interface() 44 if !reflect.DeepEqual(got, tt.want) { 45 t.Errorf("Value(%v).Interface() = %v, want %v", tt.in, got, tt.want) 46 } 47 48 if got := tt.in.IsValid(); got != (tt.want != nil) { 49 t.Errorf("Value(%v).IsValid() = %v, want %v", tt.in, got, tt.want != nil) 50 } 51 switch want := tt.want.(type) { 52 case int32: 53 if got := tt.in.Int(); got != int64(want) { 54 t.Errorf("Value(%v).Int() = %v, want %v", tt.in, got, tt.want) 55 } 56 case int64: 57 if got := tt.in.Int(); got != int64(want) { 58 t.Errorf("Value(%v).Int() = %v, want %v", tt.in, got, tt.want) 59 } 60 case uint32: 61 if got := tt.in.Uint(); got != uint64(want) { 62 t.Errorf("Value(%v).Uint() = %v, want %v", tt.in, got, tt.want) 63 } 64 case uint64: 65 if got := tt.in.Uint(); got != uint64(want) { 66 t.Errorf("Value(%v).Uint() = %v, want %v", tt.in, got, tt.want) 67 } 68 case float32: 69 if got := tt.in.Float(); got != float64(want) { 70 t.Errorf("Value(%v).Float() = %v, want %v", tt.in, got, tt.want) 71 } 72 case float64: 73 if got := tt.in.Float(); got != float64(want) { 74 t.Errorf("Value(%v).Float() = %v, want %v", tt.in, got, tt.want) 75 } 76 case string: 77 if got := tt.in.String(); got != string(want) { 78 t.Errorf("Value(%v).String() = %v, want %v", tt.in, got, tt.want) 79 } 80 case []byte: 81 if got := tt.in.Bytes(); !bytes.Equal(got, want) { 82 t.Errorf("Value(%v).Bytes() = %v, want %v", tt.in, got, tt.want) 83 } 84 case EnumNumber: 85 if got := tt.in.Enum(); got != want { 86 t.Errorf("Value(%v).Enum() = %v, want %v", tt.in, got, tt.want) 87 } 88 case Message: 89 if got := tt.in.Message(); got != want { 90 t.Errorf("Value(%v).Message() = %v, want %v", tt.in, got, tt.want) 91 } 92 case List: 93 if got := tt.in.List(); got != want { 94 t.Errorf("Value(%v).List() = %v, want %v", tt.in, got, tt.want) 95 } 96 case Map: 97 if got := tt.in.Map(); got != want { 98 t.Errorf("Value(%v).Map() = %v, want %v", tt.in, got, tt.want) 99 } 100 } 101 } 102} 103 104func TestValueEqual(t *testing.T) { 105 tests := []struct { 106 x, y Value 107 want bool 108 }{ 109 {Value{}, Value{}, true}, 110 {Value{}, ValueOfBool(true), false}, 111 {ValueOfBool(true), ValueOfBool(true), true}, 112 {ValueOfBool(true), ValueOfBool(false), false}, 113 {ValueOfBool(false), ValueOfInt32(0), false}, 114 {ValueOfInt32(0), ValueOfInt32(0), true}, 115 {ValueOfInt32(0), ValueOfInt32(1), false}, 116 {ValueOfInt32(0), ValueOfInt64(0), false}, 117 {ValueOfInt64(123), ValueOfInt64(123), true}, 118 {ValueOfFloat64(0), ValueOfFloat64(0), true}, 119 {ValueOfFloat64(math.NaN()), ValueOfFloat64(math.NaN()), true}, 120 {ValueOfFloat64(math.NaN()), ValueOfFloat64(0), false}, 121 {ValueOfFloat64(math.Inf(1)), ValueOfFloat64(math.Inf(1)), true}, 122 {ValueOfFloat64(math.Inf(-1)), ValueOfFloat64(math.Inf(1)), false}, 123 {ValueOfBytes(nil), ValueOfBytes(nil), true}, 124 {ValueOfBytes(nil), ValueOfBytes([]byte{}), true}, 125 {ValueOfBytes(nil), ValueOfBytes([]byte{1}), false}, 126 {ValueOfEnum(0), ValueOfEnum(0), true}, 127 {ValueOfEnum(0), ValueOfEnum(1), false}, 128 {ValueOfBool(false), ValueOfMessage(fakeMessage), false}, 129 {ValueOfMessage(fakeMessage), ValueOfList(fakeList), false}, 130 {ValueOfList(fakeList), ValueOfMap(fakeMap), false}, 131 {ValueOfMap(fakeMap), ValueOfMessage(fakeMessage), false}, 132 133 // Composite types are not tested here. 134 // See proto.TestEqual. 135 } 136 137 for _, tt := range tests { 138 got := tt.x.Equal(tt.y) 139 if got != tt.want { 140 t.Errorf("(%v).Equal(%v) = %v, want %v", tt.x, tt.y, got, tt.want) 141 } 142 } 143} 144 145func BenchmarkValue(b *testing.B) { 146 const testdata = "The quick brown fox jumped over the lazy dog." 147 var sink1 string 148 var sink2 Value 149 var sink3 interface{} 150 151 // Baseline measures the time to store a string into a native variable. 152 b.Run("Baseline", func(b *testing.B) { 153 b.ReportAllocs() 154 for i := 0; i < b.N; i++ { 155 sink1 = testdata[:len(testdata)%(i+1)] 156 } 157 }) 158 159 // Inline measures the time to store a string into a Value, 160 // assuming that the compiler could inline the ValueOf function call. 161 b.Run("Inline", func(b *testing.B) { 162 b.ReportAllocs() 163 for i := 0; i < b.N; i++ { 164 sink2 = valueOfString(testdata[:len(testdata)%(i+1)]) 165 } 166 }) 167 168 // Value measures the time to store a string into a Value using the general 169 // ValueOf function call. This should be identical to Inline. 170 // 171 // NOTE: As of Go1.11, this is not as efficient as Inline due to the lack 172 // of some compiler optimizations: 173 // https://golang.org/issue/22310 174 // https://golang.org/issue/25189 175 b.Run("Value", func(b *testing.B) { 176 b.ReportAllocs() 177 for i := 0; i < b.N; i++ { 178 sink2 = ValueOf(string(testdata[:len(testdata)%(i+1)])) 179 } 180 }) 181 182 // Interface measures the time to store a string into an interface. 183 b.Run("Interface", func(b *testing.B) { 184 b.ReportAllocs() 185 for i := 0; i < b.N; i++ { 186 sink3 = string(testdata[:len(testdata)%(i+1)]) 187 } 188 }) 189 190 _, _, _ = sink1, sink2, sink3 191} 192