blob: 7cd1747c6fd8e622949956d2ddd23c62762f7557 [file] [log] [blame]
Damien Neil5ec58b92019-04-30 11:36:39 -07001// Copyright 2019 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 prototest exercises protobuf reflection.
6package prototest
7
8import (
9 "bytes"
10 "fmt"
11 "math"
12 "sort"
13 "testing"
14
Damien Neild91246e2019-06-21 08:36:26 -070015 "google.golang.org/protobuf/encoding/prototext"
16 "google.golang.org/protobuf/internal/encoding/wire"
Damien Neile89e6242019-05-13 23:55:40 -070017 "google.golang.org/protobuf/proto"
18 pref "google.golang.org/protobuf/reflect/protoreflect"
Damien Neild91246e2019-06-21 08:36:26 -070019 preg "google.golang.org/protobuf/reflect/protoregistry"
Damien Neil5ec58b92019-04-30 11:36:39 -070020)
21
Joe Tsai378c1322019-04-25 23:48:08 -070022// TODO: Test read-only properties of unpopulated composite values.
23// TODO: Test invalid field descriptors or oneof descriptors.
24// TODO: This should test the functionality that can be provided by fast-paths.
Damien Neil5ec58b92019-04-30 11:36:39 -070025
Damien Neild91246e2019-06-21 08:36:26 -070026// MessageOptions configure message tests.
27type MessageOptions struct {
28 // ExtensionTypes is a list of types to test with.
29 //
30 // If nil, TestMessage will look for extension types in the global registry.
31 ExtensionTypes []pref.ExtensionType
32}
33
Joe Tsai378c1322019-04-25 23:48:08 -070034// TestMessage runs the provided m through a series of tests
35// exercising the protobuf reflection API.
Damien Neild91246e2019-06-21 08:36:26 -070036func TestMessage(t testing.TB, m proto.Message, opts MessageOptions) {
Joe Tsai378c1322019-04-25 23:48:08 -070037 md := m.ProtoReflect().Descriptor()
38 m1 := m.ProtoReflect().New()
Damien Neil5ec58b92019-04-30 11:36:39 -070039 for i := 0; i < md.Fields().Len(); i++ {
40 fd := md.Fields().Get(i)
Joe Tsai378c1322019-04-25 23:48:08 -070041 testField(t, m1, fd)
Damien Neil5ec58b92019-04-30 11:36:39 -070042 }
Damien Neild91246e2019-06-21 08:36:26 -070043 if opts.ExtensionTypes == nil {
44 preg.GlobalTypes.RangeExtensionsByMessage(md.FullName(), func(e pref.ExtensionType) bool {
45 opts.ExtensionTypes = append(opts.ExtensionTypes, e)
46 return true
47 })
48 }
49 for _, xt := range opts.ExtensionTypes {
50 testField(t, m1, xt)
51 }
Damien Neil5ec58b92019-04-30 11:36:39 -070052 for i := 0; i < md.Oneofs().Len(); i++ {
Joe Tsai378c1322019-04-25 23:48:08 -070053 testOneof(t, m1, md.Oneofs().Get(i))
Damien Neil5ec58b92019-04-30 11:36:39 -070054 }
Damien Neild91246e2019-06-21 08:36:26 -070055 testUnknown(t, m1)
Damien Neil5ec58b92019-04-30 11:36:39 -070056
57 // Test round-trip marshal/unmarshal.
Joe Tsai378c1322019-04-25 23:48:08 -070058 m2 := m.ProtoReflect().New().Interface()
59 populateMessage(m2.ProtoReflect(), 1, nil)
60 b, err := proto.Marshal(m2)
Damien Neil5ec58b92019-04-30 11:36:39 -070061 if err != nil {
Joe Tsai378c1322019-04-25 23:48:08 -070062 t.Errorf("Marshal() = %v, want nil\n%v", err, marshalText(m2))
Damien Neil5ec58b92019-04-30 11:36:39 -070063 }
Joe Tsai378c1322019-04-25 23:48:08 -070064 m3 := m.ProtoReflect().New().Interface()
65 if err := proto.Unmarshal(b, m3); err != nil {
66 t.Errorf("Unmarshal() = %v, want nil\n%v", err, marshalText(m2))
Damien Neil5ec58b92019-04-30 11:36:39 -070067 }
Joe Tsai378c1322019-04-25 23:48:08 -070068 if !proto.Equal(m2, m3) {
69 t.Errorf("round-trip marshal/unmarshal did not preserve message\nOriginal:\n%v\nNew:\n%v", marshalText(m2), marshalText(m3))
Damien Neil5ec58b92019-04-30 11:36:39 -070070 }
71}
72
73func marshalText(m proto.Message) string {
Damien Neil5c5b5312019-05-14 12:44:37 -070074 b, _ := prototext.MarshalOptions{Indent: " "}.Marshal(m)
Damien Neil5ec58b92019-04-30 11:36:39 -070075 return string(b)
76}
77
Joe Tsai378c1322019-04-25 23:48:08 -070078// testField exercises set/get/has/clear of a field.
Damien Neil5ec58b92019-04-30 11:36:39 -070079func testField(t testing.TB, m pref.Message, fd pref.FieldDescriptor) {
Damien Neil5ec58b92019-04-30 11:36:39 -070080 name := fd.FullName()
Joe Tsai378c1322019-04-25 23:48:08 -070081 num := fd.Number()
Damien Neil5ec58b92019-04-30 11:36:39 -070082
Damien Neild91246e2019-06-21 08:36:26 -070083 switch {
84 case fd.IsList():
85 testFieldList(t, m, fd)
86 case fd.IsMap():
87 testFieldMap(t, m, fd)
88 case fd.Kind() == pref.FloatKind || fd.Kind() == pref.DoubleKind:
89 testFieldFloat(t, m, fd)
90 }
91
Damien Neil5ec58b92019-04-30 11:36:39 -070092 // Set to a non-zero value, the zero value, different non-zero values.
93 for _, n := range []seed{1, 0, minVal, maxVal} {
94 v := newValue(m, fd, n, nil)
Joe Tsai378c1322019-04-25 23:48:08 -070095 m.Set(fd, v)
Damien Neil5ec58b92019-04-30 11:36:39 -070096 wantHas := true
97 if n == 0 {
98 if fd.Syntax() == pref.Proto3 && fd.Message() == nil {
99 wantHas = false
100 }
101 if fd.Cardinality() == pref.Repeated {
102 wantHas = false
103 }
Damien Neild91246e2019-06-21 08:36:26 -0700104 if fd.IsExtension() {
105 wantHas = true
106 }
Joe Tsaiac31a352019-05-13 14:32:56 -0700107 if fd.ContainingOneof() != nil {
Damien Neil5ec58b92019-04-30 11:36:39 -0700108 wantHas = true
109 }
110 }
Joe Tsai378c1322019-04-25 23:48:08 -0700111 if got, want := m.Has(fd), wantHas; got != want {
112 t.Errorf("after setting %q to %v:\nMessage.Has(%v) = %v, want %v", name, formatValue(v), num, got, want)
Damien Neil5ec58b92019-04-30 11:36:39 -0700113 }
Joe Tsai378c1322019-04-25 23:48:08 -0700114 if got, want := m.Get(fd), v; !valueEqual(got, want) {
115 t.Errorf("after setting %q:\nMessage.Get(%v) = %v, want %v", name, num, formatValue(got), formatValue(want))
Damien Neil5ec58b92019-04-30 11:36:39 -0700116 }
Damien Neild91246e2019-06-21 08:36:26 -0700117 found := false
118 m.Range(func(d pref.FieldDescriptor, got pref.Value) bool {
119 if fd != d {
120 return true
121 }
122 found = true
123 if want := v; !valueEqual(got, want) {
124 t.Errorf("after setting %q:\nMessage.Range got value %v, want %v", name, formatValue(got), formatValue(want))
125 }
126 return true
127 })
128 if got, want := wantHas, found; got != want {
129 t.Errorf("after setting %q:\nMessageRange saw field: %v, want %v", name, got, want)
130 }
Damien Neil5ec58b92019-04-30 11:36:39 -0700131 }
132
Joe Tsai378c1322019-04-25 23:48:08 -0700133 m.Clear(fd)
134 if got, want := m.Has(fd), false; got != want {
135 t.Errorf("after clearing %q:\nMessage.Has(%v) = %v, want %v", name, num, got, want)
Damien Neil5ec58b92019-04-30 11:36:39 -0700136 }
137 switch {
Joe Tsaiac31a352019-05-13 14:32:56 -0700138 case fd.IsList():
Joe Tsai378c1322019-04-25 23:48:08 -0700139 if got := m.Get(fd); got.List().Len() != 0 {
140 t.Errorf("after clearing %q:\nMessage.Get(%v) = %v, want empty list", name, num, formatValue(got))
Damien Neil5ec58b92019-04-30 11:36:39 -0700141 }
Joe Tsaiac31a352019-05-13 14:32:56 -0700142 case fd.IsMap():
Joe Tsai378c1322019-04-25 23:48:08 -0700143 if got := m.Get(fd); got.Map().Len() != 0 {
144 t.Errorf("after clearing %q:\nMessage.Get(%v) = %v, want empty list", name, num, formatValue(got))
Damien Neil5ec58b92019-04-30 11:36:39 -0700145 }
Joe Tsai378c1322019-04-25 23:48:08 -0700146 case fd.Message() == nil:
147 if got, want := m.Get(fd), fd.Default(); !valueEqual(got, want) {
148 t.Errorf("after clearing %q:\nMessage.Get(%v) = %v, want default %v", name, num, formatValue(got), formatValue(want))
Damien Neil5ec58b92019-04-30 11:36:39 -0700149 }
150 }
Damien Neild91246e2019-06-21 08:36:26 -0700151
152 // Set to the wrong type.
153 v := pref.ValueOf("")
154 if fd.Kind() == pref.StringKind {
155 v = pref.ValueOf(int32(0))
156 }
157 if !panics(func() {
158 m.Set(fd, v)
159 }) {
160 t.Errorf("setting %v to %T succeeds, want panic", name, v.Interface())
161 }
Damien Neil5ec58b92019-04-30 11:36:39 -0700162}
163
164// testFieldMap tests set/get/has/clear of entries in a map field.
165func testFieldMap(t testing.TB, m pref.Message, fd pref.FieldDescriptor) {
Damien Neil5ec58b92019-04-30 11:36:39 -0700166 name := fd.FullName()
Joe Tsai378c1322019-04-25 23:48:08 -0700167 num := fd.Number()
168
169 m.Clear(fd) // start with an empty map
170 mapv := m.Mutable(fd).Map()
Damien Neil5ec58b92019-04-30 11:36:39 -0700171
172 // Add values.
173 want := make(testMap)
174 for i, n := range []seed{1, 0, minVal, maxVal} {
Joe Tsai378c1322019-04-25 23:48:08 -0700175 if got, want := m.Has(fd), i > 0; got != want {
176 t.Errorf("after inserting %d elements to %q:\nMessage.Has(%v) = %v, want %v", i, name, num, got, want)
Damien Neil5ec58b92019-04-30 11:36:39 -0700177 }
178
179 k := newMapKey(fd, n)
180 v := newMapValue(fd, mapv, n, nil)
181 mapv.Set(k, v)
182 want.Set(k, v)
Joe Tsai378c1322019-04-25 23:48:08 -0700183 if got, want := m.Get(fd), pref.ValueOf(want); !valueEqual(got, want) {
184 t.Errorf("after inserting %d elements to %q:\nMessage.Get(%v) = %v, want %v", i, name, num, formatValue(got), formatValue(want))
Damien Neil5ec58b92019-04-30 11:36:39 -0700185 }
186 }
187
188 // Set values.
189 want.Range(func(k pref.MapKey, v pref.Value) bool {
190 nv := newMapValue(fd, mapv, 10, nil)
191 mapv.Set(k, nv)
192 want.Set(k, nv)
Joe Tsai378c1322019-04-25 23:48:08 -0700193 if got, want := m.Get(fd), pref.ValueOf(want); !valueEqual(got, want) {
194 t.Errorf("after setting element %v of %q:\nMessage.Get(%v) = %v, want %v", formatValue(k.Value()), name, num, formatValue(got), formatValue(want))
Damien Neil5ec58b92019-04-30 11:36:39 -0700195 }
196 return true
197 })
198
199 // Clear values.
200 want.Range(func(k pref.MapKey, v pref.Value) bool {
201 mapv.Clear(k)
202 want.Clear(k)
Joe Tsai378c1322019-04-25 23:48:08 -0700203 if got, want := m.Has(fd), want.Len() > 0; got != want {
204 t.Errorf("after clearing elements of %q:\nMessage.Has(%v) = %v, want %v", name, num, got, want)
Damien Neil5ec58b92019-04-30 11:36:39 -0700205 }
Joe Tsai378c1322019-04-25 23:48:08 -0700206 if got, want := m.Get(fd), pref.ValueOf(want); !valueEqual(got, want) {
207 t.Errorf("after clearing elements of %q:\nMessage.Get(%v) = %v, want %v", name, num, formatValue(got), formatValue(want))
Damien Neil5ec58b92019-04-30 11:36:39 -0700208 }
209 return true
210 })
211
212 // Non-existent map keys.
213 missingKey := newMapKey(fd, 1)
214 if got, want := mapv.Has(missingKey), false; got != want {
Joe Tsai378c1322019-04-25 23:48:08 -0700215 t.Errorf("non-existent map key in %q: Map.Has(%v) = %v, want %v", name, formatValue(missingKey.Value()), got, want)
Damien Neil5ec58b92019-04-30 11:36:39 -0700216 }
217 if got, want := mapv.Get(missingKey).IsValid(), false; got != want {
Joe Tsai378c1322019-04-25 23:48:08 -0700218 t.Errorf("non-existent map key in %q: Map.Get(%v).IsValid() = %v, want %v", name, formatValue(missingKey.Value()), got, want)
Damien Neil5ec58b92019-04-30 11:36:39 -0700219 }
220 mapv.Clear(missingKey) // noop
221}
222
223type testMap map[interface{}]pref.Value
224
225func (m testMap) Get(k pref.MapKey) pref.Value { return m[k.Interface()] }
226func (m testMap) Set(k pref.MapKey, v pref.Value) { m[k.Interface()] = v }
227func (m testMap) Has(k pref.MapKey) bool { return m.Get(k).IsValid() }
228func (m testMap) Clear(k pref.MapKey) { delete(m, k.Interface()) }
229func (m testMap) Len() int { return len(m) }
230func (m testMap) NewMessage() pref.Message { panic("unimplemented") }
231func (m testMap) Range(f func(pref.MapKey, pref.Value) bool) {
232 for k, v := range m {
233 if !f(pref.ValueOf(k).MapKey(), v) {
234 return
235 }
236 }
237}
238
239// testFieldList exercises set/get/append/truncate of values in a list.
240func testFieldList(t testing.TB, m pref.Message, fd pref.FieldDescriptor) {
Damien Neil5ec58b92019-04-30 11:36:39 -0700241 name := fd.FullName()
Joe Tsai378c1322019-04-25 23:48:08 -0700242 num := fd.Number()
243
244 m.Clear(fd) // start with an empty list
245 list := m.Mutable(fd).List()
Damien Neil5ec58b92019-04-30 11:36:39 -0700246
247 // Append values.
248 var want pref.List = &testList{}
249 for i, n := range []seed{1, 0, minVal, maxVal} {
Damien Neild91246e2019-06-21 08:36:26 -0700250 if got, want := m.Has(fd), i > 0 || fd.IsExtension(); got != want {
Joe Tsai378c1322019-04-25 23:48:08 -0700251 t.Errorf("after appending %d elements to %q:\nMessage.Has(%v) = %v, want %v", i, name, num, got, want)
Damien Neil5ec58b92019-04-30 11:36:39 -0700252 }
253 v := newListElement(fd, list, n, nil)
254 want.Append(v)
255 list.Append(v)
256
Joe Tsai378c1322019-04-25 23:48:08 -0700257 if got, want := m.Get(fd), pref.ValueOf(want); !valueEqual(got, want) {
258 t.Errorf("after appending %d elements to %q:\nMessage.Get(%v) = %v, want %v", i+1, name, num, formatValue(got), formatValue(want))
Damien Neil5ec58b92019-04-30 11:36:39 -0700259 }
260 }
261
262 // Set values.
263 for i := 0; i < want.Len(); i++ {
264 v := newListElement(fd, list, seed(i+10), nil)
265 want.Set(i, v)
266 list.Set(i, v)
Joe Tsai378c1322019-04-25 23:48:08 -0700267 if got, want := m.Get(fd), pref.ValueOf(want); !valueEqual(got, want) {
268 t.Errorf("after setting element %d of %q:\nMessage.Get(%v) = %v, want %v", i, name, num, formatValue(got), formatValue(want))
Damien Neil5ec58b92019-04-30 11:36:39 -0700269 }
270 }
271
272 // Truncate.
273 for want.Len() > 0 {
274 n := want.Len() - 1
275 want.Truncate(n)
276 list.Truncate(n)
Damien Neild91246e2019-06-21 08:36:26 -0700277 if got, want := m.Has(fd), want.Len() > 0 || fd.IsExtension(); got != want {
Joe Tsai378c1322019-04-25 23:48:08 -0700278 t.Errorf("after truncating %q to %d:\nMessage.Has(%v) = %v, want %v", name, n, num, got, want)
Damien Neil5ec58b92019-04-30 11:36:39 -0700279 }
Joe Tsai378c1322019-04-25 23:48:08 -0700280 if got, want := m.Get(fd), pref.ValueOf(want); !valueEqual(got, want) {
281 t.Errorf("after truncating %q to %d:\nMessage.Get(%v) = %v, want %v", name, n, num, formatValue(got), formatValue(want))
Damien Neil5ec58b92019-04-30 11:36:39 -0700282 }
283 }
284}
285
286type testList struct {
287 a []pref.Value
288}
289
290func (l *testList) Append(v pref.Value) { l.a = append(l.a, v) }
291func (l *testList) Get(n int) pref.Value { return l.a[n] }
292func (l *testList) Len() int { return len(l.a) }
293func (l *testList) Set(n int, v pref.Value) { l.a[n] = v }
294func (l *testList) Truncate(n int) { l.a = l.a[:n] }
295func (l *testList) NewMessage() pref.Message { panic("unimplemented") }
296
297// testFieldFloat exercises some interesting floating-point scalar field values.
298func testFieldFloat(t testing.TB, m pref.Message, fd pref.FieldDescriptor) {
Damien Neil5ec58b92019-04-30 11:36:39 -0700299 name := fd.FullName()
Joe Tsai378c1322019-04-25 23:48:08 -0700300 num := fd.Number()
301
Damien Neil5ec58b92019-04-30 11:36:39 -0700302 for _, v := range []float64{math.Inf(-1), math.Inf(1), math.NaN(), math.Copysign(0, -1)} {
303 var val pref.Value
304 if fd.Kind() == pref.FloatKind {
305 val = pref.ValueOf(float32(v))
306 } else {
307 val = pref.ValueOf(v)
308 }
Joe Tsai378c1322019-04-25 23:48:08 -0700309 m.Set(fd, val)
Damien Neil5ec58b92019-04-30 11:36:39 -0700310 // Note that Has is true for -0.
Joe Tsai378c1322019-04-25 23:48:08 -0700311 if got, want := m.Has(fd), true; got != want {
312 t.Errorf("after setting %v to %v: Message.Has(%v) = %v, want %v", name, v, num, got, want)
Damien Neil5ec58b92019-04-30 11:36:39 -0700313 }
Joe Tsai378c1322019-04-25 23:48:08 -0700314 if got, want := m.Get(fd), val; !valueEqual(got, want) {
315 t.Errorf("after setting %v: Message.Get(%v) = %v, want %v", name, num, formatValue(got), formatValue(want))
Damien Neil5ec58b92019-04-30 11:36:39 -0700316 }
317 }
318}
319
320// testOneof tests the behavior of fields in a oneof.
321func testOneof(t testing.TB, m pref.Message, od pref.OneofDescriptor) {
Damien Neila3456c92019-04-30 11:36:49 -0700322 for _, mutable := range []bool{false, true} {
323 for i := 0; i < od.Fields().Len(); i++ {
324 fda := od.Fields().Get(i)
325 if mutable {
326 // Set fields by requesting a mutable reference.
327 if !fda.IsMap() && !fda.IsList() && fda.Message() == nil {
328 continue
329 }
330 _ = m.Mutable(fda)
331 } else {
332 // Set fields explicitly.
333 m.Set(fda, newValue(m, fda, 1, nil))
334 }
335 if got, want := m.WhichOneof(od), fda; got != want {
336 t.Errorf("after setting oneof field %q:\nWhichOneof(%q) = %v, want %v", fda.FullName(), fda.Name(), got, want)
337 }
338 for j := 0; j < od.Fields().Len(); j++ {
339 fdb := od.Fields().Get(j)
340 if got, want := m.Has(fdb), i == j; got != want {
341 t.Errorf("after setting oneof field %q:\nGet(%q) = %v, want %v", fda.FullName(), fdb.FullName(), got, want)
342 }
Damien Neil5ec58b92019-04-30 11:36:39 -0700343 }
344 }
345 }
346}
347
Damien Neild91246e2019-06-21 08:36:26 -0700348// testUnknown tests the behavior of unknown fields.
349func testUnknown(t testing.TB, m pref.Message) {
350 var b []byte
351 b = wire.AppendTag(b, 1000, wire.VarintType)
352 b = wire.AppendVarint(b, 1001)
353 m.SetUnknown(pref.RawFields(b))
354 if got, want := []byte(m.GetUnknown()), b; !bytes.Equal(got, want) {
355 t.Errorf("after setting unknown fields:\nGetUnknown() = %v, want %v", got, want)
356 }
357}
358
Damien Neil5ec58b92019-04-30 11:36:39 -0700359func formatValue(v pref.Value) string {
360 switch v := v.Interface().(type) {
361 case pref.List:
362 var buf bytes.Buffer
363 buf.WriteString("list[")
364 for i := 0; i < v.Len(); i++ {
365 if i > 0 {
366 buf.WriteString(" ")
367 }
368 buf.WriteString(formatValue(v.Get(i)))
369 }
370 buf.WriteString("]")
371 return buf.String()
372 case pref.Map:
373 var buf bytes.Buffer
374 buf.WriteString("map[")
375 var keys []pref.MapKey
376 v.Range(func(k pref.MapKey, v pref.Value) bool {
377 keys = append(keys, k)
378 return true
379 })
380 sort.Slice(keys, func(i, j int) bool {
381 return keys[i].String() < keys[j].String()
382 })
383 for i, k := range keys {
384 if i > 0 {
385 buf.WriteString(" ")
386 }
387 buf.WriteString(formatValue(k.Value()))
388 buf.WriteString(":")
389 buf.WriteString(formatValue(v.Get(k)))
390 }
391 buf.WriteString("]")
392 return buf.String()
393 case pref.Message:
Damien Neil5c5b5312019-05-14 12:44:37 -0700394 b, err := prototext.Marshal(v.Interface())
Damien Neil5ec58b92019-04-30 11:36:39 -0700395 if err != nil {
396 return fmt.Sprintf("<%v>", err)
397 }
Joe Tsai0fc49f82019-05-01 12:29:25 -0700398 return fmt.Sprintf("%v{%v}", v.Descriptor().FullName(), string(b))
Damien Neil5ec58b92019-04-30 11:36:39 -0700399 case string:
400 return fmt.Sprintf("%q", v)
401 default:
402 return fmt.Sprint(v)
403 }
404}
405
406func valueEqual(a, b pref.Value) bool {
407 ai, bi := a.Interface(), b.Interface()
408 switch ai.(type) {
409 case pref.Message:
410 return proto.Equal(
411 a.Message().Interface(),
412 b.Message().Interface(),
413 )
414 case pref.List:
415 lista, listb := a.List(), b.List()
416 if lista.Len() != listb.Len() {
417 return false
418 }
419 for i := 0; i < lista.Len(); i++ {
420 if !valueEqual(lista.Get(i), listb.Get(i)) {
421 return false
422 }
423 }
424 return true
425 case pref.Map:
426 mapa, mapb := a.Map(), b.Map()
427 if mapa.Len() != mapb.Len() {
428 return false
429 }
430 equal := true
431 mapa.Range(func(k pref.MapKey, v pref.Value) bool {
432 if !valueEqual(v, mapb.Get(k)) {
433 equal = false
434 return false
435 }
436 return true
437 })
438 return equal
439 case []byte:
440 return bytes.Equal(a.Bytes(), b.Bytes())
Joe Tsaifb30a402019-05-20 15:19:14 -0700441 case float32:
Damien Neil5ec58b92019-04-30 11:36:39 -0700442 // NaNs are equal, but must be the same NaN.
Joe Tsaifb30a402019-05-20 15:19:14 -0700443 return math.Float32bits(ai.(float32)) == math.Float32bits(bi.(float32))
444 case float64:
445 // NaNs are equal, but must be the same NaN.
446 return math.Float64bits(ai.(float64)) == math.Float64bits(bi.(float64))
Damien Neil5ec58b92019-04-30 11:36:39 -0700447 default:
448 return ai == bi
449 }
450}
451
452// A seed is used to vary the content of a value.
453//
454// A seed of 0 is the zero value. Messages do not have a zero-value; a 0-seeded messages
455// is unpopulated.
456//
457// A seed of minVal or maxVal is the least or greatest value of the value type.
458type seed int
459
460const (
461 minVal seed = -1
462 maxVal seed = -2
463)
464
465// newValue returns a new value assignable to a field.
466//
467// The stack parameter is used to avoid infinite recursion when populating circular
468// data structures.
Joe Tsai0fc49f82019-05-01 12:29:25 -0700469func newValue(m pref.Message, fd pref.FieldDescriptor, n seed, stack []pref.MessageDescriptor) pref.Value {
Damien Neil5ec58b92019-04-30 11:36:39 -0700470 switch {
Joe Tsaiac31a352019-05-13 14:32:56 -0700471 case fd.IsList():
Joe Tsai378c1322019-04-25 23:48:08 -0700472 list := m.New().Mutable(fd).List()
Joe Tsaiac31a352019-05-13 14:32:56 -0700473 if n == 0 {
474 return pref.ValueOf(list)
475 }
476 list.Append(newListElement(fd, list, 0, stack))
477 list.Append(newListElement(fd, list, minVal, stack))
478 list.Append(newListElement(fd, list, maxVal, stack))
479 list.Append(newListElement(fd, list, n, stack))
480 return pref.ValueOf(list)
Damien Neil5ec58b92019-04-30 11:36:39 -0700481 case fd.IsMap():
Joe Tsai378c1322019-04-25 23:48:08 -0700482 mapv := m.New().Mutable(fd).Map()
Damien Neil5ec58b92019-04-30 11:36:39 -0700483 if n == 0 {
484 return pref.ValueOf(mapv)
485 }
486 mapv.Set(newMapKey(fd, 0), newMapValue(fd, mapv, 0, stack))
487 mapv.Set(newMapKey(fd, minVal), newMapValue(fd, mapv, minVal, stack))
488 mapv.Set(newMapKey(fd, maxVal), newMapValue(fd, mapv, maxVal, stack))
489 mapv.Set(newMapKey(fd, n), newMapValue(fd, mapv, 10*n, stack))
490 return pref.ValueOf(mapv)
Damien Neil5ec58b92019-04-30 11:36:39 -0700491 case fd.Message() != nil:
Joe Tsai378c1322019-04-25 23:48:08 -0700492 return populateMessage(m.Mutable(fd).Message(), n, stack)
Damien Neil5ec58b92019-04-30 11:36:39 -0700493 default:
494 return newScalarValue(fd, n)
495 }
496}
497
Joe Tsai0fc49f82019-05-01 12:29:25 -0700498func newListElement(fd pref.FieldDescriptor, list pref.List, n seed, stack []pref.MessageDescriptor) pref.Value {
Damien Neil5ec58b92019-04-30 11:36:39 -0700499 if fd.Message() == nil {
500 return newScalarValue(fd, n)
501 }
502 return populateMessage(list.NewMessage(), n, stack)
503}
504
505func newMapKey(fd pref.FieldDescriptor, n seed) pref.MapKey {
Joe Tsaiac31a352019-05-13 14:32:56 -0700506 kd := fd.MapKey()
Damien Neil5ec58b92019-04-30 11:36:39 -0700507 return newScalarValue(kd, n).MapKey()
508}
509
Joe Tsai0fc49f82019-05-01 12:29:25 -0700510func newMapValue(fd pref.FieldDescriptor, mapv pref.Map, n seed, stack []pref.MessageDescriptor) pref.Value {
Joe Tsaiac31a352019-05-13 14:32:56 -0700511 vd := fd.MapValue()
Damien Neil5ec58b92019-04-30 11:36:39 -0700512 if vd.Message() == nil {
513 return newScalarValue(vd, n)
514 }
515 return populateMessage(mapv.NewMessage(), n, stack)
516}
517
518func newScalarValue(fd pref.FieldDescriptor, n seed) pref.Value {
519 switch fd.Kind() {
520 case pref.BoolKind:
521 return pref.ValueOf(n != 0)
522 case pref.EnumKind:
Joe Tsai378c1322019-04-25 23:48:08 -0700523 // TODO: use actual value
Damien Neil5ec58b92019-04-30 11:36:39 -0700524 return pref.ValueOf(pref.EnumNumber(n))
525 case pref.Int32Kind, pref.Sint32Kind, pref.Sfixed32Kind:
526 switch n {
527 case minVal:
528 return pref.ValueOf(int32(math.MinInt32))
529 case maxVal:
530 return pref.ValueOf(int32(math.MaxInt32))
531 default:
532 return pref.ValueOf(int32(n))
533 }
534 case pref.Uint32Kind, pref.Fixed32Kind:
535 switch n {
536 case minVal:
537 // Only use 0 for the zero value.
538 return pref.ValueOf(uint32(1))
539 case maxVal:
540 return pref.ValueOf(uint32(math.MaxInt32))
541 default:
542 return pref.ValueOf(uint32(n))
543 }
544 case pref.Int64Kind, pref.Sint64Kind, pref.Sfixed64Kind:
545 switch n {
546 case minVal:
547 return pref.ValueOf(int64(math.MinInt64))
548 case maxVal:
549 return pref.ValueOf(int64(math.MaxInt64))
550 default:
551 return pref.ValueOf(int64(n))
552 }
553 case pref.Uint64Kind, pref.Fixed64Kind:
554 switch n {
555 case minVal:
556 // Only use 0 for the zero value.
557 return pref.ValueOf(uint64(1))
558 case maxVal:
559 return pref.ValueOf(uint64(math.MaxInt64))
560 default:
561 return pref.ValueOf(uint64(n))
562 }
563 case pref.FloatKind:
564 switch n {
565 case minVal:
566 return pref.ValueOf(float32(math.SmallestNonzeroFloat32))
567 case maxVal:
568 return pref.ValueOf(float32(math.MaxFloat32))
569 default:
570 return pref.ValueOf(1.5 * float32(n))
571 }
572 case pref.DoubleKind:
573 switch n {
574 case minVal:
575 return pref.ValueOf(float64(math.SmallestNonzeroFloat64))
576 case maxVal:
577 return pref.ValueOf(float64(math.MaxFloat64))
578 default:
579 return pref.ValueOf(1.5 * float64(n))
580 }
581 case pref.StringKind:
582 if n == 0 {
583 return pref.ValueOf("")
584 }
585 return pref.ValueOf(fmt.Sprintf("%d", n))
586 case pref.BytesKind:
587 if n == 0 {
588 return pref.ValueOf([]byte(nil))
589 }
590 return pref.ValueOf([]byte{byte(n >> 24), byte(n >> 16), byte(n >> 8), byte(n)})
591 }
592 panic("unhandled kind")
593}
594
Joe Tsai0fc49f82019-05-01 12:29:25 -0700595func populateMessage(m pref.Message, n seed, stack []pref.MessageDescriptor) pref.Value {
Damien Neil5ec58b92019-04-30 11:36:39 -0700596 if n == 0 {
597 return pref.ValueOf(m)
598 }
Joe Tsai0fc49f82019-05-01 12:29:25 -0700599 md := m.Descriptor()
Damien Neil5ec58b92019-04-30 11:36:39 -0700600 for _, x := range stack {
601 if md == x {
602 return pref.ValueOf(m)
603 }
604 }
605 stack = append(stack, md)
Damien Neil5ec58b92019-04-30 11:36:39 -0700606 for i := 0; i < md.Fields().Len(); i++ {
607 fd := md.Fields().Get(i)
608 if fd.IsWeak() {
609 continue
610 }
Joe Tsai378c1322019-04-25 23:48:08 -0700611 m.Set(fd, newValue(m, fd, 10*n+seed(i), stack))
Damien Neil5ec58b92019-04-30 11:36:39 -0700612 }
613 return pref.ValueOf(m)
614}
Damien Neild91246e2019-06-21 08:36:26 -0700615
616func panics(f func()) (didPanic bool) {
617 defer func() {
618 if err := recover(); err != nil {
619 didPanic = true
620 }
621 }()
622 f()
623 return false
624}