blob: 0c6b700ed78d99303f5caecebdf7151842ee90d0 [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"
Joe Tsaib7695fa2020-01-04 09:09:04 -080012 "reflect"
Damien Neil5ec58b92019-04-30 11:36:39 -070013 "sort"
14 "testing"
15
Damien Neild91246e2019-06-21 08:36:26 -070016 "google.golang.org/protobuf/encoding/prototext"
17 "google.golang.org/protobuf/internal/encoding/wire"
Damien Neile89e6242019-05-13 23:55:40 -070018 "google.golang.org/protobuf/proto"
19 pref "google.golang.org/protobuf/reflect/protoreflect"
Damien Neild91246e2019-06-21 08:36:26 -070020 preg "google.golang.org/protobuf/reflect/protoregistry"
Damien Neil5ec58b92019-04-30 11:36:39 -070021)
22
Joe Tsai378c1322019-04-25 23:48:08 -070023// 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
Damien Neil290ceea2019-07-15 13:39:43 -070032
33 // Resolver is used for looking up types when unmarshaling extension fields.
34 // If nil, this defaults to using protoregistry.GlobalTypes.
35 Resolver interface {
36 preg.ExtensionTypeResolver
37 }
Damien Neild91246e2019-06-21 08:36:26 -070038}
39
Joe Tsai93bccf72020-02-10 11:14:03 -080040// TODO(blocks): TestMessage should not take in MessageOptions,
41// but have a MessageOptions.Test method instead.
42
Joe Tsai378c1322019-04-25 23:48:08 -070043// TestMessage runs the provided m through a series of tests
44// exercising the protobuf reflection API.
Damien Neild91246e2019-06-21 08:36:26 -070045func TestMessage(t testing.TB, m proto.Message, opts MessageOptions) {
Joe Tsaib7695fa2020-01-04 09:09:04 -080046 testType(t, m)
47
Joe Tsai378c1322019-04-25 23:48:08 -070048 md := m.ProtoReflect().Descriptor()
49 m1 := m.ProtoReflect().New()
Damien Neil5ec58b92019-04-30 11:36:39 -070050 for i := 0; i < md.Fields().Len(); i++ {
51 fd := md.Fields().Get(i)
Joe Tsai378c1322019-04-25 23:48:08 -070052 testField(t, m1, fd)
Damien Neil5ec58b92019-04-30 11:36:39 -070053 }
Damien Neild91246e2019-06-21 08:36:26 -070054 if opts.ExtensionTypes == nil {
55 preg.GlobalTypes.RangeExtensionsByMessage(md.FullName(), func(e pref.ExtensionType) bool {
56 opts.ExtensionTypes = append(opts.ExtensionTypes, e)
57 return true
58 })
59 }
60 for _, xt := range opts.ExtensionTypes {
Damien Neil79bfdbe2019-08-28 11:08:22 -070061 testField(t, m1, xt.TypeDescriptor())
Damien Neild91246e2019-06-21 08:36:26 -070062 }
Damien Neil5ec58b92019-04-30 11:36:39 -070063 for i := 0; i < md.Oneofs().Len(); i++ {
Joe Tsai378c1322019-04-25 23:48:08 -070064 testOneof(t, m1, md.Oneofs().Get(i))
Damien Neil5ec58b92019-04-30 11:36:39 -070065 }
Damien Neild91246e2019-06-21 08:36:26 -070066 testUnknown(t, m1)
Damien Neil5ec58b92019-04-30 11:36:39 -070067
68 // Test round-trip marshal/unmarshal.
Joe Tsai378c1322019-04-25 23:48:08 -070069 m2 := m.ProtoReflect().New().Interface()
70 populateMessage(m2.ProtoReflect(), 1, nil)
Damien Neil290ceea2019-07-15 13:39:43 -070071 for _, xt := range opts.ExtensionTypes {
72 m2.ProtoReflect().Set(xt.TypeDescriptor(), newValue(m2.ProtoReflect(), xt.TypeDescriptor(), 1, nil))
73 }
74 b, err := proto.MarshalOptions{
75 AllowPartial: true,
76 }.Marshal(m2)
Damien Neil5ec58b92019-04-30 11:36:39 -070077 if err != nil {
Joe Tsai74b14602020-01-06 15:44:09 -080078 t.Errorf("Marshal() = %v, want nil\n%v", err, prototext.Format(m2))
Damien Neil5ec58b92019-04-30 11:36:39 -070079 }
Joe Tsai378c1322019-04-25 23:48:08 -070080 m3 := m.ProtoReflect().New().Interface()
Damien Neil290ceea2019-07-15 13:39:43 -070081 if err := (proto.UnmarshalOptions{
82 AllowPartial: true,
83 Resolver: opts.Resolver,
84 }.Unmarshal(b, m3)); err != nil {
Joe Tsai74b14602020-01-06 15:44:09 -080085 t.Errorf("Unmarshal() = %v, want nil\n%v", err, prototext.Format(m2))
Damien Neil5ec58b92019-04-30 11:36:39 -070086 }
Joe Tsai378c1322019-04-25 23:48:08 -070087 if !proto.Equal(m2, m3) {
Joe Tsai74b14602020-01-06 15:44:09 -080088 t.Errorf("round-trip marshal/unmarshal did not preserve message\nOriginal:\n%v\nNew:\n%v", prototext.Format(m2), prototext.Format(m3))
Damien Neil5ec58b92019-04-30 11:36:39 -070089 }
90}
91
Joe Tsaib7695fa2020-01-04 09:09:04 -080092func testType(t testing.TB, m proto.Message) {
93 want := reflect.TypeOf(m)
94 if got := reflect.TypeOf(m.ProtoReflect().Interface()); got != want {
95 t.Errorf("type mismatch: reflect.TypeOf(m) != reflect.TypeOf(m.ProtoReflect().Interface()): %v != %v", got, want)
96 }
97 if got := reflect.TypeOf(m.ProtoReflect().New().Interface()); got != want {
98 t.Errorf("type mismatch: reflect.TypeOf(m) != reflect.TypeOf(m.ProtoReflect().New().Interface()): %v != %v", got, want)
99 }
100 if got := reflect.TypeOf(m.ProtoReflect().Type().Zero().Interface()); got != want {
101 t.Errorf("type mismatch: reflect.TypeOf(m) != reflect.TypeOf(m.ProtoReflect().Type().Zero().Interface()): %v != %v", got, want)
102 }
103}
104
Joe Tsai378c1322019-04-25 23:48:08 -0700105// testField exercises set/get/has/clear of a field.
Damien Neil5ec58b92019-04-30 11:36:39 -0700106func testField(t testing.TB, m pref.Message, fd pref.FieldDescriptor) {
Damien Neil5ec58b92019-04-30 11:36:39 -0700107 name := fd.FullName()
Joe Tsai378c1322019-04-25 23:48:08 -0700108 num := fd.Number()
Damien Neil5ec58b92019-04-30 11:36:39 -0700109
Damien Neild91246e2019-06-21 08:36:26 -0700110 switch {
111 case fd.IsList():
112 testFieldList(t, m, fd)
113 case fd.IsMap():
114 testFieldMap(t, m, fd)
Damien Neilf5274512019-08-05 10:48:38 -0700115 case fd.Message() != nil:
116 default:
117 if got, want := m.NewField(fd), fd.Default(); !valueEqual(got, want) {
118 t.Errorf("Message.NewField(%v) = %v, want default value %v", name, formatValue(got), formatValue(want))
119 }
120 if fd.Kind() == pref.FloatKind || fd.Kind() == pref.DoubleKind {
121 testFieldFloat(t, m, fd)
122 }
Damien Neild91246e2019-06-21 08:36:26 -0700123 }
124
Damien Neil5ec58b92019-04-30 11:36:39 -0700125 // Set to a non-zero value, the zero value, different non-zero values.
126 for _, n := range []seed{1, 0, minVal, maxVal} {
127 v := newValue(m, fd, n, nil)
Joe Tsai378c1322019-04-25 23:48:08 -0700128 m.Set(fd, v)
Damien Neil5ec58b92019-04-30 11:36:39 -0700129 wantHas := true
130 if n == 0 {
131 if fd.Syntax() == pref.Proto3 && fd.Message() == nil {
132 wantHas = false
133 }
Damien Neild91246e2019-06-21 08:36:26 -0700134 if fd.IsExtension() {
135 wantHas = true
136 }
Damien Neila0a54b82019-11-01 15:18:36 -0700137 if fd.Cardinality() == pref.Repeated {
138 wantHas = false
139 }
Joe Tsaiac31a352019-05-13 14:32:56 -0700140 if fd.ContainingOneof() != nil {
Damien Neil5ec58b92019-04-30 11:36:39 -0700141 wantHas = true
142 }
143 }
Damien Neilf5274512019-08-05 10:48:38 -0700144 if fd.Syntax() == pref.Proto3 && fd.Cardinality() != pref.Repeated && fd.ContainingOneof() == nil && fd.Kind() == pref.EnumKind && v.Enum() == 0 {
145 wantHas = false
146 }
Joe Tsai378c1322019-04-25 23:48:08 -0700147 if got, want := m.Has(fd), wantHas; got != want {
148 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 -0700149 }
Joe Tsai378c1322019-04-25 23:48:08 -0700150 if got, want := m.Get(fd), v; !valueEqual(got, want) {
151 t.Errorf("after setting %q:\nMessage.Get(%v) = %v, want %v", name, num, formatValue(got), formatValue(want))
Damien Neil5ec58b92019-04-30 11:36:39 -0700152 }
Damien Neild91246e2019-06-21 08:36:26 -0700153 found := false
154 m.Range(func(d pref.FieldDescriptor, got pref.Value) bool {
155 if fd != d {
156 return true
157 }
158 found = true
159 if want := v; !valueEqual(got, want) {
160 t.Errorf("after setting %q:\nMessage.Range got value %v, want %v", name, formatValue(got), formatValue(want))
161 }
162 return true
163 })
164 if got, want := wantHas, found; got != want {
165 t.Errorf("after setting %q:\nMessageRange saw field: %v, want %v", name, got, want)
166 }
Damien Neil5ec58b92019-04-30 11:36:39 -0700167 }
168
Joe Tsai378c1322019-04-25 23:48:08 -0700169 m.Clear(fd)
170 if got, want := m.Has(fd), false; got != want {
171 t.Errorf("after clearing %q:\nMessage.Has(%v) = %v, want %v", name, num, got, want)
Damien Neil5ec58b92019-04-30 11:36:39 -0700172 }
173 switch {
Joe Tsaiac31a352019-05-13 14:32:56 -0700174 case fd.IsList():
Joe Tsai378c1322019-04-25 23:48:08 -0700175 if got := m.Get(fd); got.List().Len() != 0 {
176 t.Errorf("after clearing %q:\nMessage.Get(%v) = %v, want empty list", name, num, formatValue(got))
Damien Neil5ec58b92019-04-30 11:36:39 -0700177 }
Joe Tsaiac31a352019-05-13 14:32:56 -0700178 case fd.IsMap():
Joe Tsai378c1322019-04-25 23:48:08 -0700179 if got := m.Get(fd); got.Map().Len() != 0 {
Damien Neil290ceea2019-07-15 13:39:43 -0700180 t.Errorf("after clearing %q:\nMessage.Get(%v) = %v, want empty map", name, num, formatValue(got))
Damien Neil5ec58b92019-04-30 11:36:39 -0700181 }
Joe Tsai378c1322019-04-25 23:48:08 -0700182 case fd.Message() == nil:
183 if got, want := m.Get(fd), fd.Default(); !valueEqual(got, want) {
184 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 -0700185 }
186 }
Damien Neild91246e2019-06-21 08:36:26 -0700187
Damien Neil290ceea2019-07-15 13:39:43 -0700188 // Set to the default value.
189 switch {
190 case fd.IsList() || fd.IsMap():
Damien Neil9f1165c2019-11-26 13:31:38 -0800191 m.Set(fd, m.Mutable(fd))
Damien Neila0a54b82019-11-01 15:18:36 -0700192 if got, want := m.Has(fd), (fd.IsExtension() && fd.Cardinality() != pref.Repeated) || fd.ContainingOneof() != nil; got != want {
Damien Neil290ceea2019-07-15 13:39:43 -0700193 t.Errorf("after setting %q to default:\nMessage.Has(%v) = %v, want %v", name, num, got, want)
194 }
195 case fd.Message() == nil:
196 m.Set(fd, m.Get(fd))
197 if got, want := m.Get(fd), fd.Default(); !valueEqual(got, want) {
198 t.Errorf("after setting %q to default:\nMessage.Get(%v) = %v, want default %v", name, num, formatValue(got), formatValue(want))
199 }
200 }
201 m.Clear(fd)
202
Damien Neild91246e2019-06-21 08:36:26 -0700203 // Set to the wrong type.
Joe Tsai84177c92019-09-17 13:38:48 -0700204 v := pref.ValueOfString("")
Damien Neild91246e2019-06-21 08:36:26 -0700205 if fd.Kind() == pref.StringKind {
Joe Tsai84177c92019-09-17 13:38:48 -0700206 v = pref.ValueOfInt32(0)
Damien Neild91246e2019-06-21 08:36:26 -0700207 }
208 if !panics(func() {
209 m.Set(fd, v)
210 }) {
211 t.Errorf("setting %v to %T succeeds, want panic", name, v.Interface())
212 }
Damien Neil5ec58b92019-04-30 11:36:39 -0700213}
214
215// testFieldMap tests set/get/has/clear of entries in a map field.
216func testFieldMap(t testing.TB, m pref.Message, fd pref.FieldDescriptor) {
Damien Neil5ec58b92019-04-30 11:36:39 -0700217 name := fd.FullName()
Joe Tsai378c1322019-04-25 23:48:08 -0700218 num := fd.Number()
219
Damien Neilf5274512019-08-05 10:48:38 -0700220 // New values.
Joe Tsai378c1322019-04-25 23:48:08 -0700221 m.Clear(fd) // start with an empty map
Damien Neilf5274512019-08-05 10:48:38 -0700222 mapv := m.Get(fd).Map()
Damien Neil9f1165c2019-11-26 13:31:38 -0800223 if mapv.IsValid() {
224 t.Errorf("after clearing field: message.Get(%v).IsValid() = true, want false", name)
225 }
Damien Neilf5274512019-08-05 10:48:38 -0700226 if got, want := mapv.NewValue(), newMapValue(fd, mapv, 0, nil); !valueEqual(got, want) {
227 t.Errorf("message.Get(%v).NewValue() = %v, want %v", name, formatValue(got), formatValue(want))
228 }
Damien Neil9f1165c2019-11-26 13:31:38 -0800229 if !panics(func() {
230 m.Set(fd, pref.ValueOfMap(mapv))
231 }) {
232 t.Errorf("message.Set(%v, <invalid>) does not panic", name)
233 }
234 if !panics(func() {
235 mapv.Set(newMapKey(fd, 0), newMapValue(fd, mapv, 0, nil))
236 }) {
237 t.Errorf("message.Get(%v).Set(...) of invalid map does not panic", name)
238 }
Damien Neilf5274512019-08-05 10:48:38 -0700239 mapv = m.Mutable(fd).Map() // mutable map
Damien Neil9f1165c2019-11-26 13:31:38 -0800240 if !mapv.IsValid() {
241 t.Errorf("message.Mutable(%v).IsValid() = false, want true", name)
242 }
Damien Neilf5274512019-08-05 10:48:38 -0700243 if got, want := mapv.NewValue(), newMapValue(fd, mapv, 0, nil); !valueEqual(got, want) {
244 t.Errorf("message.Mutable(%v).NewValue() = %v, want %v", name, formatValue(got), formatValue(want))
245 }
Damien Neil5ec58b92019-04-30 11:36:39 -0700246
247 // Add values.
248 want := make(testMap)
249 for i, n := range []seed{1, 0, minVal, maxVal} {
Joe Tsai378c1322019-04-25 23:48:08 -0700250 if got, want := m.Has(fd), i > 0; got != want {
251 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 -0700252 }
253
254 k := newMapKey(fd, n)
255 v := newMapValue(fd, mapv, n, nil)
256 mapv.Set(k, v)
257 want.Set(k, v)
Joe Tsai84177c92019-09-17 13:38:48 -0700258 if got, want := m.Get(fd), pref.ValueOfMap(want); !valueEqual(got, want) {
Joe Tsai378c1322019-04-25 23:48:08 -0700259 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 -0700260 }
261 }
262
263 // Set values.
264 want.Range(func(k pref.MapKey, v pref.Value) bool {
265 nv := newMapValue(fd, mapv, 10, nil)
266 mapv.Set(k, nv)
267 want.Set(k, nv)
Joe Tsai84177c92019-09-17 13:38:48 -0700268 if got, want := m.Get(fd), pref.ValueOfMap(want); !valueEqual(got, want) {
Joe Tsai378c1322019-04-25 23:48:08 -0700269 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 -0700270 }
271 return true
272 })
273
274 // Clear values.
275 want.Range(func(k pref.MapKey, v pref.Value) bool {
276 mapv.Clear(k)
277 want.Clear(k)
Joe Tsai378c1322019-04-25 23:48:08 -0700278 if got, want := m.Has(fd), want.Len() > 0; got != want {
279 t.Errorf("after clearing elements of %q:\nMessage.Has(%v) = %v, want %v", name, num, got, want)
Damien Neil5ec58b92019-04-30 11:36:39 -0700280 }
Joe Tsai84177c92019-09-17 13:38:48 -0700281 if got, want := m.Get(fd), pref.ValueOfMap(want); !valueEqual(got, want) {
Joe Tsai378c1322019-04-25 23:48:08 -0700282 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 -0700283 }
284 return true
285 })
Damien Neil9f1165c2019-11-26 13:31:38 -0800286 if mapv := m.Get(fd).Map(); mapv.IsValid() {
287 t.Errorf("after clearing all elements: message.Get(%v).IsValid() = true, want false %v", name, formatValue(pref.ValueOfMap(mapv)))
288 }
Damien Neil5ec58b92019-04-30 11:36:39 -0700289
290 // Non-existent map keys.
291 missingKey := newMapKey(fd, 1)
292 if got, want := mapv.Has(missingKey), false; got != want {
Joe Tsai378c1322019-04-25 23:48:08 -0700293 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 -0700294 }
295 if got, want := mapv.Get(missingKey).IsValid(), false; got != want {
Joe Tsai378c1322019-04-25 23:48:08 -0700296 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 -0700297 }
298 mapv.Clear(missingKey) // noop
Damien Neil3dbd95a2020-02-19 10:48:36 -0800299
300 // Mutable.
301 if fd.MapValue().Message() == nil {
302 if !panics(func() {
303 mapv.Mutable(newMapKey(fd, 1))
304 }) {
305 t.Errorf("Mutable on %q succeeds, want panic", name)
306 }
307 } else {
308 k := newMapKey(fd, 1)
309 v := mapv.Mutable(k)
310 if got, want := mapv.Len(), 1; got != want {
311 t.Errorf("after Mutable on %q, Map.Len() = %v, want %v", name, got, want)
312 }
313 populateMessage(v.Message(), 1, nil)
314 if !valueEqual(mapv.Get(k), v) {
315 t.Errorf("after Mutable on %q, changing new mutable value does not change map entry", name)
316 }
317 mapv.Clear(k)
318 }
Damien Neil5ec58b92019-04-30 11:36:39 -0700319}
320
321type testMap map[interface{}]pref.Value
322
Damien Neil3dbd95a2020-02-19 10:48:36 -0800323func (m testMap) Get(k pref.MapKey) pref.Value { return m[k.Interface()] }
324func (m testMap) Set(k pref.MapKey, v pref.Value) { m[k.Interface()] = v }
325func (m testMap) Has(k pref.MapKey) bool { return m.Get(k).IsValid() }
326func (m testMap) Clear(k pref.MapKey) { delete(m, k.Interface()) }
327func (m testMap) Mutable(k pref.MapKey) pref.Value { panic("unimplemented") }
328func (m testMap) Len() int { return len(m) }
329func (m testMap) NewValue() pref.Value { panic("unimplemented") }
Damien Neil5ec58b92019-04-30 11:36:39 -0700330func (m testMap) Range(f func(pref.MapKey, pref.Value) bool) {
331 for k, v := range m {
332 if !f(pref.ValueOf(k).MapKey(), v) {
333 return
334 }
335 }
336}
Damien Neil82886da2019-11-26 13:27:24 -0800337func (m testMap) IsValid() bool { return true }
Damien Neil5ec58b92019-04-30 11:36:39 -0700338
339// testFieldList exercises set/get/append/truncate of values in a list.
340func testFieldList(t testing.TB, m pref.Message, fd pref.FieldDescriptor) {
Damien Neil5ec58b92019-04-30 11:36:39 -0700341 name := fd.FullName()
Joe Tsai378c1322019-04-25 23:48:08 -0700342 num := fd.Number()
343
344 m.Clear(fd) // start with an empty list
Damien Neilf5274512019-08-05 10:48:38 -0700345 list := m.Get(fd).List()
Damien Neil9f1165c2019-11-26 13:31:38 -0800346 if list.IsValid() {
347 t.Errorf("message.Get(%v).IsValid() = true, want false", name)
348 }
349 if !panics(func() {
350 m.Set(fd, pref.ValueOfList(list))
351 }) {
352 t.Errorf("message.Set(%v, <invalid>) does not panic", name)
353 }
354 if !panics(func() {
355 list.Append(newListElement(fd, list, 0, nil))
356 }) {
357 t.Errorf("message.Get(%v).Append(...) of invalid list does not panic", name)
358 }
Damien Neilf5274512019-08-05 10:48:38 -0700359 if got, want := list.NewElement(), newListElement(fd, list, 0, nil); !valueEqual(got, want) {
360 t.Errorf("message.Get(%v).NewElement() = %v, want %v", name, formatValue(got), formatValue(want))
361 }
362 list = m.Mutable(fd).List() // mutable list
Damien Neil9f1165c2019-11-26 13:31:38 -0800363 if !list.IsValid() {
364 t.Errorf("message.Get(%v).IsValid() = false, want true", name)
365 }
Damien Neilf5274512019-08-05 10:48:38 -0700366 if got, want := list.NewElement(), newListElement(fd, list, 0, nil); !valueEqual(got, want) {
367 t.Errorf("message.Mutable(%v).NewElement() = %v, want %v", name, formatValue(got), formatValue(want))
368 }
Damien Neil5ec58b92019-04-30 11:36:39 -0700369
370 // Append values.
371 var want pref.List = &testList{}
372 for i, n := range []seed{1, 0, minVal, maxVal} {
Damien Neila0a54b82019-11-01 15:18:36 -0700373 if got, want := m.Has(fd), i > 0; got != want {
Joe Tsai378c1322019-04-25 23:48:08 -0700374 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 -0700375 }
376 v := newListElement(fd, list, n, nil)
377 want.Append(v)
378 list.Append(v)
379
Joe Tsai84177c92019-09-17 13:38:48 -0700380 if got, want := m.Get(fd), pref.ValueOfList(want); !valueEqual(got, want) {
Joe Tsai378c1322019-04-25 23:48:08 -0700381 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 -0700382 }
383 }
384
385 // Set values.
386 for i := 0; i < want.Len(); i++ {
387 v := newListElement(fd, list, seed(i+10), nil)
388 want.Set(i, v)
389 list.Set(i, v)
Joe Tsai84177c92019-09-17 13:38:48 -0700390 if got, want := m.Get(fd), pref.ValueOfList(want); !valueEqual(got, want) {
Joe Tsai378c1322019-04-25 23:48:08 -0700391 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 -0700392 }
393 }
394
395 // Truncate.
396 for want.Len() > 0 {
397 n := want.Len() - 1
398 want.Truncate(n)
399 list.Truncate(n)
Damien Neila0a54b82019-11-01 15:18:36 -0700400 if got, want := m.Has(fd), want.Len() > 0; got != want {
Joe Tsai378c1322019-04-25 23:48:08 -0700401 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 -0700402 }
Joe Tsai84177c92019-09-17 13:38:48 -0700403 if got, want := m.Get(fd), pref.ValueOfList(want); !valueEqual(got, want) {
Joe Tsai378c1322019-04-25 23:48:08 -0700404 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 -0700405 }
406 }
Damien Neil3dbd95a2020-02-19 10:48:36 -0800407
408 // AppendMutable.
409 if fd.Message() == nil {
410 if !panics(func() {
411 list.AppendMutable()
412 }) {
413 t.Errorf("AppendMutable on %q succeeds, want panic", name)
414 }
415 } else {
416 v := list.AppendMutable()
417 if got, want := list.Len(), 1; got != want {
418 t.Errorf("after AppendMutable on %q, list.Len() = %v, want %v", name, got, want)
419 }
420 populateMessage(v.Message(), 1, nil)
421 if !valueEqual(list.Get(0), v) {
422 t.Errorf("after AppendMutable on %q, changing new mutable value does not change list item 0", name)
423 }
424 want.Truncate(0)
425 }
Damien Neil5ec58b92019-04-30 11:36:39 -0700426}
427
428type testList struct {
429 a []pref.Value
430}
431
Damien Neil3dbd95a2020-02-19 10:48:36 -0800432func (l *testList) Append(v pref.Value) { l.a = append(l.a, v) }
433func (l *testList) AppendMutable() pref.Value { panic("unimplemented") }
434func (l *testList) Get(n int) pref.Value { return l.a[n] }
435func (l *testList) Len() int { return len(l.a) }
436func (l *testList) Set(n int, v pref.Value) { l.a[n] = v }
437func (l *testList) Truncate(n int) { l.a = l.a[:n] }
438func (l *testList) NewElement() pref.Value { panic("unimplemented") }
439func (l *testList) IsValid() bool { return true }
Damien Neil5ec58b92019-04-30 11:36:39 -0700440
441// testFieldFloat exercises some interesting floating-point scalar field values.
442func testFieldFloat(t testing.TB, m pref.Message, fd pref.FieldDescriptor) {
Damien Neil5ec58b92019-04-30 11:36:39 -0700443 name := fd.FullName()
Joe Tsai378c1322019-04-25 23:48:08 -0700444 num := fd.Number()
445
Damien Neil5ec58b92019-04-30 11:36:39 -0700446 for _, v := range []float64{math.Inf(-1), math.Inf(1), math.NaN(), math.Copysign(0, -1)} {
447 var val pref.Value
448 if fd.Kind() == pref.FloatKind {
Joe Tsai84177c92019-09-17 13:38:48 -0700449 val = pref.ValueOfFloat32(float32(v))
Damien Neil5ec58b92019-04-30 11:36:39 -0700450 } else {
Joe Tsai84177c92019-09-17 13:38:48 -0700451 val = pref.ValueOfFloat64(float64(v))
Damien Neil5ec58b92019-04-30 11:36:39 -0700452 }
Joe Tsai378c1322019-04-25 23:48:08 -0700453 m.Set(fd, val)
Damien Neil5ec58b92019-04-30 11:36:39 -0700454 // Note that Has is true for -0.
Joe Tsai378c1322019-04-25 23:48:08 -0700455 if got, want := m.Has(fd), true; got != want {
456 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 -0700457 }
Joe Tsai378c1322019-04-25 23:48:08 -0700458 if got, want := m.Get(fd), val; !valueEqual(got, want) {
459 t.Errorf("after setting %v: Message.Get(%v) = %v, want %v", name, num, formatValue(got), formatValue(want))
Damien Neil5ec58b92019-04-30 11:36:39 -0700460 }
461 }
462}
463
464// testOneof tests the behavior of fields in a oneof.
465func testOneof(t testing.TB, m pref.Message, od pref.OneofDescriptor) {
Damien Neila3456c92019-04-30 11:36:49 -0700466 for _, mutable := range []bool{false, true} {
467 for i := 0; i < od.Fields().Len(); i++ {
468 fda := od.Fields().Get(i)
469 if mutable {
470 // Set fields by requesting a mutable reference.
471 if !fda.IsMap() && !fda.IsList() && fda.Message() == nil {
472 continue
473 }
474 _ = m.Mutable(fda)
475 } else {
476 // Set fields explicitly.
477 m.Set(fda, newValue(m, fda, 1, nil))
478 }
479 if got, want := m.WhichOneof(od), fda; got != want {
480 t.Errorf("after setting oneof field %q:\nWhichOneof(%q) = %v, want %v", fda.FullName(), fda.Name(), got, want)
481 }
482 for j := 0; j < od.Fields().Len(); j++ {
483 fdb := od.Fields().Get(j)
484 if got, want := m.Has(fdb), i == j; got != want {
485 t.Errorf("after setting oneof field %q:\nGet(%q) = %v, want %v", fda.FullName(), fdb.FullName(), got, want)
486 }
Damien Neil5ec58b92019-04-30 11:36:39 -0700487 }
488 }
489 }
490}
491
Damien Neild91246e2019-06-21 08:36:26 -0700492// testUnknown tests the behavior of unknown fields.
493func testUnknown(t testing.TB, m pref.Message) {
494 var b []byte
495 b = wire.AppendTag(b, 1000, wire.VarintType)
496 b = wire.AppendVarint(b, 1001)
497 m.SetUnknown(pref.RawFields(b))
498 if got, want := []byte(m.GetUnknown()), b; !bytes.Equal(got, want) {
499 t.Errorf("after setting unknown fields:\nGetUnknown() = %v, want %v", got, want)
500 }
501}
502
Damien Neil5ec58b92019-04-30 11:36:39 -0700503func formatValue(v pref.Value) string {
504 switch v := v.Interface().(type) {
505 case pref.List:
506 var buf bytes.Buffer
507 buf.WriteString("list[")
508 for i := 0; i < v.Len(); i++ {
509 if i > 0 {
510 buf.WriteString(" ")
511 }
512 buf.WriteString(formatValue(v.Get(i)))
513 }
514 buf.WriteString("]")
515 return buf.String()
516 case pref.Map:
517 var buf bytes.Buffer
518 buf.WriteString("map[")
519 var keys []pref.MapKey
520 v.Range(func(k pref.MapKey, v pref.Value) bool {
521 keys = append(keys, k)
522 return true
523 })
524 sort.Slice(keys, func(i, j int) bool {
525 return keys[i].String() < keys[j].String()
526 })
527 for i, k := range keys {
528 if i > 0 {
529 buf.WriteString(" ")
530 }
531 buf.WriteString(formatValue(k.Value()))
532 buf.WriteString(":")
533 buf.WriteString(formatValue(v.Get(k)))
534 }
535 buf.WriteString("]")
536 return buf.String()
537 case pref.Message:
Damien Neil5c5b5312019-05-14 12:44:37 -0700538 b, err := prototext.Marshal(v.Interface())
Damien Neil5ec58b92019-04-30 11:36:39 -0700539 if err != nil {
540 return fmt.Sprintf("<%v>", err)
541 }
Joe Tsai0fc49f82019-05-01 12:29:25 -0700542 return fmt.Sprintf("%v{%v}", v.Descriptor().FullName(), string(b))
Damien Neil5ec58b92019-04-30 11:36:39 -0700543 case string:
544 return fmt.Sprintf("%q", v)
545 default:
546 return fmt.Sprint(v)
547 }
548}
549
550func valueEqual(a, b pref.Value) bool {
551 ai, bi := a.Interface(), b.Interface()
552 switch ai.(type) {
553 case pref.Message:
554 return proto.Equal(
555 a.Message().Interface(),
556 b.Message().Interface(),
557 )
558 case pref.List:
559 lista, listb := a.List(), b.List()
560 if lista.Len() != listb.Len() {
561 return false
562 }
563 for i := 0; i < lista.Len(); i++ {
564 if !valueEqual(lista.Get(i), listb.Get(i)) {
565 return false
566 }
567 }
568 return true
569 case pref.Map:
570 mapa, mapb := a.Map(), b.Map()
571 if mapa.Len() != mapb.Len() {
572 return false
573 }
574 equal := true
575 mapa.Range(func(k pref.MapKey, v pref.Value) bool {
576 if !valueEqual(v, mapb.Get(k)) {
577 equal = false
578 return false
579 }
580 return true
581 })
582 return equal
583 case []byte:
584 return bytes.Equal(a.Bytes(), b.Bytes())
Joe Tsaifb30a402019-05-20 15:19:14 -0700585 case float32:
Damien Neil5ec58b92019-04-30 11:36:39 -0700586 // NaNs are equal, but must be the same NaN.
Joe Tsaifb30a402019-05-20 15:19:14 -0700587 return math.Float32bits(ai.(float32)) == math.Float32bits(bi.(float32))
588 case float64:
589 // NaNs are equal, but must be the same NaN.
590 return math.Float64bits(ai.(float64)) == math.Float64bits(bi.(float64))
Damien Neil5ec58b92019-04-30 11:36:39 -0700591 default:
592 return ai == bi
593 }
594}
595
596// A seed is used to vary the content of a value.
597//
598// A seed of 0 is the zero value. Messages do not have a zero-value; a 0-seeded messages
599// is unpopulated.
600//
601// A seed of minVal or maxVal is the least or greatest value of the value type.
602type seed int
603
604const (
605 minVal seed = -1
606 maxVal seed = -2
607)
608
Damien Neilf5274512019-08-05 10:48:38 -0700609// newSeed creates new seed values from a base, for example to create seeds for the
610// elements in a list. If the input seed is minVal or maxVal, so is the output.
611func newSeed(n seed, adjust ...int) seed {
612 switch n {
613 case minVal, maxVal:
614 return n
615 }
616 for _, a := range adjust {
617 n = 10*n + seed(a)
618 }
619 return n
620}
621
Damien Neil5ec58b92019-04-30 11:36:39 -0700622// newValue returns a new value assignable to a field.
623//
624// The stack parameter is used to avoid infinite recursion when populating circular
625// data structures.
Joe Tsai0fc49f82019-05-01 12:29:25 -0700626func newValue(m pref.Message, fd pref.FieldDescriptor, n seed, stack []pref.MessageDescriptor) pref.Value {
Damien Neil5ec58b92019-04-30 11:36:39 -0700627 switch {
Joe Tsaiac31a352019-05-13 14:32:56 -0700628 case fd.IsList():
Joe Tsaiac31a352019-05-13 14:32:56 -0700629 if n == 0 {
Damien Neil9f1165c2019-11-26 13:31:38 -0800630 return m.New().Mutable(fd)
Joe Tsaiac31a352019-05-13 14:32:56 -0700631 }
Damien Neil290ceea2019-07-15 13:39:43 -0700632 list := m.NewField(fd).List()
Joe Tsaiac31a352019-05-13 14:32:56 -0700633 list.Append(newListElement(fd, list, 0, stack))
634 list.Append(newListElement(fd, list, minVal, stack))
635 list.Append(newListElement(fd, list, maxVal, stack))
636 list.Append(newListElement(fd, list, n, stack))
Joe Tsai84177c92019-09-17 13:38:48 -0700637 return pref.ValueOfList(list)
Damien Neil5ec58b92019-04-30 11:36:39 -0700638 case fd.IsMap():
Damien Neil5ec58b92019-04-30 11:36:39 -0700639 if n == 0 {
Damien Neil9f1165c2019-11-26 13:31:38 -0800640 return m.New().Mutable(fd)
Damien Neil5ec58b92019-04-30 11:36:39 -0700641 }
Damien Neil290ceea2019-07-15 13:39:43 -0700642 mapv := m.NewField(fd).Map()
Damien Neil5ec58b92019-04-30 11:36:39 -0700643 mapv.Set(newMapKey(fd, 0), newMapValue(fd, mapv, 0, stack))
644 mapv.Set(newMapKey(fd, minVal), newMapValue(fd, mapv, minVal, stack))
645 mapv.Set(newMapKey(fd, maxVal), newMapValue(fd, mapv, maxVal, stack))
Damien Neilf5274512019-08-05 10:48:38 -0700646 mapv.Set(newMapKey(fd, n), newMapValue(fd, mapv, newSeed(n, 0), stack))
Joe Tsai84177c92019-09-17 13:38:48 -0700647 return pref.ValueOfMap(mapv)
Damien Neil5ec58b92019-04-30 11:36:39 -0700648 case fd.Message() != nil:
Damien Neilf5274512019-08-05 10:48:38 -0700649 return populateMessage(m.NewField(fd).Message(), n, stack)
Damien Neil5ec58b92019-04-30 11:36:39 -0700650 default:
651 return newScalarValue(fd, n)
652 }
653}
654
Joe Tsai0fc49f82019-05-01 12:29:25 -0700655func newListElement(fd pref.FieldDescriptor, list pref.List, n seed, stack []pref.MessageDescriptor) pref.Value {
Damien Neil5ec58b92019-04-30 11:36:39 -0700656 if fd.Message() == nil {
657 return newScalarValue(fd, n)
658 }
Damien Neilf5274512019-08-05 10:48:38 -0700659 return populateMessage(list.NewElement().Message(), n, stack)
Damien Neil5ec58b92019-04-30 11:36:39 -0700660}
661
662func newMapKey(fd pref.FieldDescriptor, n seed) pref.MapKey {
Joe Tsaiac31a352019-05-13 14:32:56 -0700663 kd := fd.MapKey()
Damien Neil5ec58b92019-04-30 11:36:39 -0700664 return newScalarValue(kd, n).MapKey()
665}
666
Joe Tsai0fc49f82019-05-01 12:29:25 -0700667func newMapValue(fd pref.FieldDescriptor, mapv pref.Map, n seed, stack []pref.MessageDescriptor) pref.Value {
Joe Tsaiac31a352019-05-13 14:32:56 -0700668 vd := fd.MapValue()
Damien Neil5ec58b92019-04-30 11:36:39 -0700669 if vd.Message() == nil {
670 return newScalarValue(vd, n)
671 }
Damien Neilf5274512019-08-05 10:48:38 -0700672 return populateMessage(mapv.NewValue().Message(), n, stack)
Damien Neil5ec58b92019-04-30 11:36:39 -0700673}
674
675func newScalarValue(fd pref.FieldDescriptor, n seed) pref.Value {
676 switch fd.Kind() {
677 case pref.BoolKind:
Joe Tsai84177c92019-09-17 13:38:48 -0700678 return pref.ValueOfBool(n != 0)
Damien Neil5ec58b92019-04-30 11:36:39 -0700679 case pref.EnumKind:
Damien Neilf5274512019-08-05 10:48:38 -0700680 vals := fd.Enum().Values()
681 var i int
682 switch n {
683 case minVal:
684 i = 0
685 case maxVal:
686 i = vals.Len() - 1
687 default:
688 i = int(n) % vals.Len()
689 }
Joe Tsai84177c92019-09-17 13:38:48 -0700690 return pref.ValueOfEnum(vals.Get(i).Number())
Damien Neil5ec58b92019-04-30 11:36:39 -0700691 case pref.Int32Kind, pref.Sint32Kind, pref.Sfixed32Kind:
692 switch n {
693 case minVal:
Joe Tsai84177c92019-09-17 13:38:48 -0700694 return pref.ValueOfInt32(math.MinInt32)
Damien Neil5ec58b92019-04-30 11:36:39 -0700695 case maxVal:
Joe Tsai84177c92019-09-17 13:38:48 -0700696 return pref.ValueOfInt32(math.MaxInt32)
Damien Neil5ec58b92019-04-30 11:36:39 -0700697 default:
Joe Tsai84177c92019-09-17 13:38:48 -0700698 return pref.ValueOfInt32(int32(n))
Damien Neil5ec58b92019-04-30 11:36:39 -0700699 }
700 case pref.Uint32Kind, pref.Fixed32Kind:
701 switch n {
702 case minVal:
703 // Only use 0 for the zero value.
Joe Tsai84177c92019-09-17 13:38:48 -0700704 return pref.ValueOfUint32(1)
Damien Neil5ec58b92019-04-30 11:36:39 -0700705 case maxVal:
Joe Tsai84177c92019-09-17 13:38:48 -0700706 return pref.ValueOfUint32(math.MaxInt32)
Damien Neil5ec58b92019-04-30 11:36:39 -0700707 default:
Joe Tsai84177c92019-09-17 13:38:48 -0700708 return pref.ValueOfUint32(uint32(n))
Damien Neil5ec58b92019-04-30 11:36:39 -0700709 }
710 case pref.Int64Kind, pref.Sint64Kind, pref.Sfixed64Kind:
711 switch n {
712 case minVal:
Joe Tsai84177c92019-09-17 13:38:48 -0700713 return pref.ValueOfInt64(math.MinInt64)
Damien Neil5ec58b92019-04-30 11:36:39 -0700714 case maxVal:
Joe Tsai84177c92019-09-17 13:38:48 -0700715 return pref.ValueOfInt64(math.MaxInt64)
Damien Neil5ec58b92019-04-30 11:36:39 -0700716 default:
Joe Tsai84177c92019-09-17 13:38:48 -0700717 return pref.ValueOfInt64(int64(n))
Damien Neil5ec58b92019-04-30 11:36:39 -0700718 }
719 case pref.Uint64Kind, pref.Fixed64Kind:
720 switch n {
721 case minVal:
722 // Only use 0 for the zero value.
Joe Tsai84177c92019-09-17 13:38:48 -0700723 return pref.ValueOfUint64(1)
Damien Neil5ec58b92019-04-30 11:36:39 -0700724 case maxVal:
Joe Tsai84177c92019-09-17 13:38:48 -0700725 return pref.ValueOfUint64(math.MaxInt64)
Damien Neil5ec58b92019-04-30 11:36:39 -0700726 default:
Joe Tsai84177c92019-09-17 13:38:48 -0700727 return pref.ValueOfUint64(uint64(n))
Damien Neil5ec58b92019-04-30 11:36:39 -0700728 }
729 case pref.FloatKind:
730 switch n {
731 case minVal:
Joe Tsai84177c92019-09-17 13:38:48 -0700732 return pref.ValueOfFloat32(math.SmallestNonzeroFloat32)
Damien Neil5ec58b92019-04-30 11:36:39 -0700733 case maxVal:
Joe Tsai84177c92019-09-17 13:38:48 -0700734 return pref.ValueOfFloat32(math.MaxFloat32)
Damien Neil5ec58b92019-04-30 11:36:39 -0700735 default:
Joe Tsai84177c92019-09-17 13:38:48 -0700736 return pref.ValueOfFloat32(1.5 * float32(n))
Damien Neil5ec58b92019-04-30 11:36:39 -0700737 }
738 case pref.DoubleKind:
739 switch n {
740 case minVal:
Joe Tsai84177c92019-09-17 13:38:48 -0700741 return pref.ValueOfFloat64(math.SmallestNonzeroFloat64)
Damien Neil5ec58b92019-04-30 11:36:39 -0700742 case maxVal:
Joe Tsai84177c92019-09-17 13:38:48 -0700743 return pref.ValueOfFloat64(math.MaxFloat64)
Damien Neil5ec58b92019-04-30 11:36:39 -0700744 default:
Joe Tsai84177c92019-09-17 13:38:48 -0700745 return pref.ValueOfFloat64(1.5 * float64(n))
Damien Neil5ec58b92019-04-30 11:36:39 -0700746 }
747 case pref.StringKind:
748 if n == 0 {
Joe Tsai84177c92019-09-17 13:38:48 -0700749 return pref.ValueOfString("")
Damien Neil5ec58b92019-04-30 11:36:39 -0700750 }
Joe Tsai84177c92019-09-17 13:38:48 -0700751 return pref.ValueOfString(fmt.Sprintf("%d", n))
Damien Neil5ec58b92019-04-30 11:36:39 -0700752 case pref.BytesKind:
753 if n == 0 {
Joe Tsai84177c92019-09-17 13:38:48 -0700754 return pref.ValueOfBytes(nil)
Damien Neil5ec58b92019-04-30 11:36:39 -0700755 }
Joe Tsai84177c92019-09-17 13:38:48 -0700756 return pref.ValueOfBytes([]byte{byte(n >> 24), byte(n >> 16), byte(n >> 8), byte(n)})
Damien Neil5ec58b92019-04-30 11:36:39 -0700757 }
758 panic("unhandled kind")
759}
760
Joe Tsai0fc49f82019-05-01 12:29:25 -0700761func populateMessage(m pref.Message, n seed, stack []pref.MessageDescriptor) pref.Value {
Damien Neil5ec58b92019-04-30 11:36:39 -0700762 if n == 0 {
Joe Tsai84177c92019-09-17 13:38:48 -0700763 return pref.ValueOfMessage(m)
Damien Neil5ec58b92019-04-30 11:36:39 -0700764 }
Joe Tsai0fc49f82019-05-01 12:29:25 -0700765 md := m.Descriptor()
Damien Neil5ec58b92019-04-30 11:36:39 -0700766 for _, x := range stack {
767 if md == x {
Joe Tsai84177c92019-09-17 13:38:48 -0700768 return pref.ValueOfMessage(m)
Damien Neil5ec58b92019-04-30 11:36:39 -0700769 }
770 }
771 stack = append(stack, md)
Damien Neil5ec58b92019-04-30 11:36:39 -0700772 for i := 0; i < md.Fields().Len(); i++ {
773 fd := md.Fields().Get(i)
774 if fd.IsWeak() {
775 continue
776 }
Damien Neilf5274512019-08-05 10:48:38 -0700777 m.Set(fd, newValue(m, fd, newSeed(n, i), stack))
Damien Neil5ec58b92019-04-30 11:36:39 -0700778 }
Joe Tsai84177c92019-09-17 13:38:48 -0700779 return pref.ValueOfMessage(m)
Damien Neil5ec58b92019-04-30 11:36:39 -0700780}
Damien Neild91246e2019-06-21 08:36:26 -0700781
782func panics(f func()) (didPanic bool) {
783 defer func() {
784 if err := recover(); err != nil {
785 didPanic = true
786 }
787 }()
788 f()
789 return false
790}