Joe Tsai | dbc9a12 | 2018-08-03 17:13:23 -0700 | [diff] [blame] | 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 | |
| 5 | package protoreflect |
| 6 | |
| 7 | import ( |
| 8 | "bytes" |
| 9 | "math" |
| 10 | "reflect" |
| 11 | "testing" |
| 12 | ) |
| 13 | |
| 14 | func TestValue(t *testing.T) { |
| 15 | fakeMessage := new(struct{ Message }) |
Joe Tsai | 4b7aff6 | 2018-11-14 14:05:19 -0800 | [diff] [blame] | 16 | fakeList := new(struct{ List }) |
Joe Tsai | dbc9a12 | 2018-08-03 17:13:23 -0700 | [diff] [blame] | 17 | fakeMap := new(struct{ Map }) |
| 18 | |
| 19 | tests := []struct { |
| 20 | in Value |
| 21 | want interface{} |
| 22 | }{ |
Joe Tsai | dbc9a12 | 2018-08-03 17:13:23 -0700 | [diff] [blame] | 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}, |
Joe Tsai | 4b7aff6 | 2018-11-14 14:05:19 -0800 | [diff] [blame] | 35 | {in: ValueOf(fakeList), want: fakeList}, |
Joe Tsai | dbc9a12 | 2018-08-03 17:13:23 -0700 | [diff] [blame] | 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 | |
Joe Tsai | 812d913 | 2018-09-12 11:26:15 -0700 | [diff] [blame] | 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) |
Joe Tsai | dbc9a12 | 2018-08-03 17:13:23 -0700 | [diff] [blame] | 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 | } |
Joe Tsai | 4b7aff6 | 2018-11-14 14:05:19 -0800 | [diff] [blame] | 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 | } |
Joe Tsai | dbc9a12 | 2018-08-03 17:13:23 -0700 | [diff] [blame] | 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 | } |
Joe Tsai | dbc9a12 | 2018-08-03 17:13:23 -0700 | [diff] [blame] | 97 | } |
| 98 | } |
| 99 | } |
| 100 | |
| 101 | func 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 | } |