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