|  | // Copyright 2018 The Go Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style | 
|  | // license that can be found in the LICENSE file. | 
|  |  | 
|  | package protoreflect | 
|  |  | 
|  | import ( | 
|  | "bytes" | 
|  | "math" | 
|  | "reflect" | 
|  | "testing" | 
|  | ) | 
|  |  | 
|  | var ( | 
|  | fakeMessage = new(struct{ Message }) | 
|  | fakeList    = new(struct{ List }) | 
|  | fakeMap     = new(struct{ Map }) | 
|  | ) | 
|  |  | 
|  | func TestValue(t *testing.T) { | 
|  |  | 
|  | tests := []struct { | 
|  | in   Value | 
|  | want any | 
|  | }{ | 
|  | {in: Value{}}, | 
|  | {in: ValueOf(nil)}, | 
|  | {in: ValueOf(true), want: true}, | 
|  | {in: ValueOf(int32(math.MaxInt32)), want: int32(math.MaxInt32)}, | 
|  | {in: ValueOf(int64(math.MaxInt64)), want: int64(math.MaxInt64)}, | 
|  | {in: ValueOf(uint32(math.MaxUint32)), want: uint32(math.MaxUint32)}, | 
|  | {in: ValueOf(uint64(math.MaxUint64)), want: uint64(math.MaxUint64)}, | 
|  | {in: ValueOf(float32(math.MaxFloat32)), want: float32(math.MaxFloat32)}, | 
|  | {in: ValueOf(float64(math.MaxFloat64)), want: float64(math.MaxFloat64)}, | 
|  | {in: ValueOf(string("hello")), want: string("hello")}, | 
|  | {in: ValueOf([]byte("hello")), want: []byte("hello")}, | 
|  | {in: ValueOf(fakeMessage), want: fakeMessage}, | 
|  | {in: ValueOf(fakeList), want: fakeList}, | 
|  | {in: ValueOf(fakeMap), want: fakeMap}, | 
|  | } | 
|  |  | 
|  | for _, tt := range tests { | 
|  | got := tt.in.Interface() | 
|  | if !reflect.DeepEqual(got, tt.want) { | 
|  | t.Errorf("Value(%v).Interface() = %v, want %v", tt.in, got, tt.want) | 
|  | } | 
|  |  | 
|  | if got := tt.in.IsValid(); got != (tt.want != nil) { | 
|  | t.Errorf("Value(%v).IsValid() = %v, want %v", tt.in, got, tt.want != nil) | 
|  | } | 
|  | switch want := tt.want.(type) { | 
|  | case int32: | 
|  | if got := tt.in.Int(); got != int64(want) { | 
|  | t.Errorf("Value(%v).Int() = %v, want %v", tt.in, got, tt.want) | 
|  | } | 
|  | case int64: | 
|  | if got := tt.in.Int(); got != int64(want) { | 
|  | t.Errorf("Value(%v).Int() = %v, want %v", tt.in, got, tt.want) | 
|  | } | 
|  | case uint32: | 
|  | if got := tt.in.Uint(); got != uint64(want) { | 
|  | t.Errorf("Value(%v).Uint() = %v, want %v", tt.in, got, tt.want) | 
|  | } | 
|  | case uint64: | 
|  | if got := tt.in.Uint(); got != uint64(want) { | 
|  | t.Errorf("Value(%v).Uint() = %v, want %v", tt.in, got, tt.want) | 
|  | } | 
|  | case float32: | 
|  | if got := tt.in.Float(); got != float64(want) { | 
|  | t.Errorf("Value(%v).Float() = %v, want %v", tt.in, got, tt.want) | 
|  | } | 
|  | case float64: | 
|  | if got := tt.in.Float(); got != float64(want) { | 
|  | t.Errorf("Value(%v).Float() = %v, want %v", tt.in, got, tt.want) | 
|  | } | 
|  | case string: | 
|  | if got := tt.in.String(); got != string(want) { | 
|  | t.Errorf("Value(%v).String() = %v, want %v", tt.in, got, tt.want) | 
|  | } | 
|  | case []byte: | 
|  | if got := tt.in.Bytes(); !bytes.Equal(got, want) { | 
|  | t.Errorf("Value(%v).Bytes() = %v, want %v", tt.in, got, tt.want) | 
|  | } | 
|  | case EnumNumber: | 
|  | if got := tt.in.Enum(); got != want { | 
|  | t.Errorf("Value(%v).Enum() = %v, want %v", tt.in, got, tt.want) | 
|  | } | 
|  | case Message: | 
|  | if got := tt.in.Message(); got != want { | 
|  | t.Errorf("Value(%v).Message() = %v, want %v", tt.in, got, tt.want) | 
|  | } | 
|  | case List: | 
|  | if got := tt.in.List(); got != want { | 
|  | t.Errorf("Value(%v).List() = %v, want %v", tt.in, got, tt.want) | 
|  | } | 
|  | case Map: | 
|  | if got := tt.in.Map(); got != want { | 
|  | t.Errorf("Value(%v).Map() = %v, want %v", tt.in, got, tt.want) | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestValueEqual(t *testing.T) { | 
|  | tests := []struct { | 
|  | x, y Value | 
|  | want bool | 
|  | }{ | 
|  | {Value{}, Value{}, true}, | 
|  | {Value{}, ValueOfBool(true), false}, | 
|  | {ValueOfBool(true), ValueOfBool(true), true}, | 
|  | {ValueOfBool(true), ValueOfBool(false), false}, | 
|  | {ValueOfBool(false), ValueOfInt32(0), false}, | 
|  | {ValueOfInt32(0), ValueOfInt32(0), true}, | 
|  | {ValueOfInt32(0), ValueOfInt32(1), false}, | 
|  | {ValueOfInt32(0), ValueOfInt64(0), false}, | 
|  | {ValueOfInt64(123), ValueOfInt64(123), true}, | 
|  | {ValueOfFloat64(0), ValueOfFloat64(0), true}, | 
|  | {ValueOfFloat64(math.NaN()), ValueOfFloat64(math.NaN()), true}, | 
|  | {ValueOfFloat64(math.NaN()), ValueOfFloat64(0), false}, | 
|  | {ValueOfFloat64(math.Inf(1)), ValueOfFloat64(math.Inf(1)), true}, | 
|  | {ValueOfFloat64(math.Inf(-1)), ValueOfFloat64(math.Inf(1)), false}, | 
|  | {ValueOfBytes(nil), ValueOfBytes(nil), true}, | 
|  | {ValueOfBytes(nil), ValueOfBytes([]byte{}), true}, | 
|  | {ValueOfBytes(nil), ValueOfBytes([]byte{1}), false}, | 
|  | {ValueOfEnum(0), ValueOfEnum(0), true}, | 
|  | {ValueOfEnum(0), ValueOfEnum(1), false}, | 
|  | {ValueOfBool(false), ValueOfMessage(fakeMessage), false}, | 
|  | {ValueOfMessage(fakeMessage), ValueOfList(fakeList), false}, | 
|  | {ValueOfList(fakeList), ValueOfMap(fakeMap), false}, | 
|  | {ValueOfMap(fakeMap), ValueOfMessage(fakeMessage), false}, | 
|  |  | 
|  | // Composite types are not tested here. | 
|  | // See proto.TestEqual. | 
|  | } | 
|  |  | 
|  | for _, tt := range tests { | 
|  | got := tt.x.Equal(tt.y) | 
|  | if got != tt.want { | 
|  | t.Errorf("(%v).Equal(%v) = %v, want %v", tt.x, tt.y, got, tt.want) | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | func BenchmarkValue(b *testing.B) { | 
|  | const testdata = "The quick brown fox jumped over the lazy dog." | 
|  | var sink1 string | 
|  | var sink2 Value | 
|  | var sink3 any | 
|  |  | 
|  | // Baseline measures the time to store a string into a native variable. | 
|  | b.Run("Baseline", func(b *testing.B) { | 
|  | b.ReportAllocs() | 
|  | for i := 0; i < b.N; i++ { | 
|  | sink1 = testdata[:len(testdata)%(i+1)] | 
|  | } | 
|  | }) | 
|  |  | 
|  | // Inline measures the time to store a string into a Value, | 
|  | // assuming that the compiler could inline the ValueOf function call. | 
|  | b.Run("Inline", func(b *testing.B) { | 
|  | b.ReportAllocs() | 
|  | for i := 0; i < b.N; i++ { | 
|  | sink2 = valueOfString(testdata[:len(testdata)%(i+1)]) | 
|  | } | 
|  | }) | 
|  |  | 
|  | // Value measures the time to store a string into a Value using the general | 
|  | // ValueOf function call. This should be identical to Inline. | 
|  | // | 
|  | // NOTE: As of Go1.11, this is not as efficient as Inline due to the lack | 
|  | // of some compiler optimizations: | 
|  | //	https://golang.org/issue/22310 | 
|  | //	https://golang.org/issue/25189 | 
|  | b.Run("Value", func(b *testing.B) { | 
|  | b.ReportAllocs() | 
|  | for i := 0; i < b.N; i++ { | 
|  | sink2 = ValueOf(string(testdata[:len(testdata)%(i+1)])) | 
|  | } | 
|  | }) | 
|  |  | 
|  | // Interface measures the time to store a string into an interface. | 
|  | b.Run("Interface", func(b *testing.B) { | 
|  | b.ReportAllocs() | 
|  | for i := 0; i < b.N; i++ { | 
|  | sink3 = string(testdata[:len(testdata)%(i+1)]) | 
|  | } | 
|  | }) | 
|  |  | 
|  | _, _, _ = sink1, sink2, sink3 | 
|  | } |