internal/impl: implement Vector fields

Generate functions for wrapping []T to implement protoreflect.Vector.
This implementation uses Go reflection instead to provide a single implementation
that can handle all Go slice types.

The test harness was greatly expanded to be able to test vectors (in addition
to messages and maps in the near future).

Change-Id: I0106c175f84a1e7e0a0a5b0e02e2489b70b0d177
Reviewed-on: https://go-review.googlesource.com/c/135339
Reviewed-by: Damien Neil <dneil@google.com>
This commit is contained in:
Joe Tsai 2018-09-13 13:24:35 -07:00
parent 162c12703c
commit 91e1466d6f
3 changed files with 546 additions and 250 deletions

View File

@ -215,7 +215,7 @@ func (m *message) KnownFields() pref.KnownFields {
func (m *message) UnknownFields() pref.UnknownFields {
return (*unknownFields)(m)
}
func (m *message) Unwrap() interface{} {
func (m *message) Unwrap() interface{} { // TODO: unexport?
return m.p.asType(m.mi.goType.Elem()).Interface()
}
func (m *message) Interface() pref.ProtoMessage {
@ -266,7 +266,7 @@ func (fs *knownFields) Set(n pref.FieldNumber, v pref.Value) {
return
}
// TODO: Handle extension fields.
panic("invalid field")
panic(fmt.Sprintf("invalid field: %d", n))
}
func (fs *knownFields) Clear(n pref.FieldNumber) {
if fi := fs.mi.fields[n]; fi != nil {
@ -274,14 +274,14 @@ func (fs *knownFields) Clear(n pref.FieldNumber) {
return
}
// TODO: Handle extension fields.
panic("invalid field")
panic(fmt.Sprintf("invalid field: %d", n))
}
func (fs *knownFields) Mutable(n pref.FieldNumber) pref.Mutable {
if fi := fs.mi.fields[n]; fi != nil {
return fi.mutable(fs.p)
}
// TODO: Handle extension fields.
panic("invalid field")
panic(fmt.Sprintf("invalid field: %d", n))
}
func (fs *knownFields) Range(f func(pref.FieldNumber, pref.Value) bool) {
for n, fi := range fs.mi.fields {

View File

@ -41,10 +41,79 @@ func fieldInfoForMap(fd pref.FieldDescriptor, fs reflect.StructField) fieldInfo
}
func fieldInfoForVector(fd pref.FieldDescriptor, fs reflect.StructField) fieldInfo {
// TODO: support vector fields.
panic(fmt.Sprintf("invalid field: %v", fd))
ft := fs.Type
if ft.Kind() != reflect.Slice {
panic(fmt.Sprintf("invalid type: got %v, want slice kind", ft))
}
conv := matchGoTypePBKind(ft.Elem(), fd.Kind())
fieldOffset := offsetOf(fs)
// TODO: Implement unsafe fast path?
return fieldInfo{
has: func(p pointer) bool {
rv := p.apply(fieldOffset).asType(fs.Type).Elem()
return rv.Len() > 0
},
get: func(p pointer) pref.Value {
rv := p.apply(fieldOffset).asType(fs.Type).Elem()
return pref.ValueOf(vectorReflect{rv, conv})
},
set: func(p pointer, v pref.Value) {
rv := p.apply(fieldOffset).asType(fs.Type).Elem()
rv.Set(v.Vector().(vectorReflect).v)
},
clear: func(p pointer) {
rv := p.apply(fieldOffset).asType(fs.Type).Elem()
rv.Set(reflect.Zero(rv.Type()))
},
mutable: func(p pointer) pref.Mutable {
rv := p.apply(fieldOffset).asType(fs.Type).Elem()
return vectorReflect{rv, conv}
},
}
}
type vectorReflect struct {
v reflect.Value // addressable []T
conv converter
}
func (vs vectorReflect) Len() int {
return vs.v.Len()
}
func (vs vectorReflect) Get(i int) pref.Value {
return vs.conv.toPB(vs.v.Index(i))
}
func (vs vectorReflect) Set(i int, v pref.Value) {
vs.v.Index(i).Set(vs.conv.toGo(v))
}
func (vs vectorReflect) Append(v pref.Value) {
vs.v.Set(reflect.Append(vs.v, vs.conv.toGo(v)))
}
func (vs vectorReflect) Mutable(i int) pref.Mutable {
// Mutable is only valid for messages and panics for other kinds.
rv := vs.v.Index(i)
if rv.IsNil() {
pv := pref.ValueOf(vs.conv.newMessage())
rv.Set(vs.conv.toGo(pv))
}
return rv.Interface().(pref.Message)
}
func (vs vectorReflect) MutableAppend() pref.Mutable {
// MutableAppend is only valid for messages and panics for other kinds.
pv := pref.ValueOf(vs.conv.newMessage())
vs.v.Set(reflect.Append(vs.v, vs.conv.toGo(pv)))
return vs.v.Index(vs.Len() - 1).Interface().(pref.Message)
}
func (vs vectorReflect) Truncate(i int) {
vs.v.Set(vs.v.Slice(0, i))
}
func (vs vectorReflect) Unwrap() interface{} { // TODO: unexport?
return vs.v.Interface()
}
func (vs vectorReflect) ProtoMutable() {}
var _ pref.Vector = vectorReflect{}
var emptyBytes = reflect.ValueOf([]byte{})
func fieldInfoForScalar(fd pref.FieldDescriptor, fs reflect.StructField) fieldInfo {
@ -221,8 +290,9 @@ func matchGoTypePBKind(t reflect.Type, k pref.Kind) converter {
// converter provides functions for converting to/from Go reflect.Value types
// and protobuf protoreflect.Value types.
type converter struct {
toPB func(reflect.Value) pref.Value
toGo func(pref.Value) reflect.Value
toPB func(reflect.Value) pref.Value
toGo func(pref.Value) reflect.Value
newMessage func() pref.Message
}
func makeScalarConverter(goType, pbType reflect.Type) converter {

View File

@ -5,10 +5,13 @@
package impl
import (
"reflect"
"fmt"
"math"
"strings"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
pref "github.com/golang/protobuf/v2/reflect/protoreflect"
ptype "github.com/golang/protobuf/v2/reflect/prototype"
@ -22,6 +25,8 @@ func mustMakeMessageDesc(t ptype.StandaloneMessage) pref.MessageDescriptor {
return md
}
var V = pref.ValueOf
type (
MyBool bool
MyInt32 int32
@ -32,266 +37,487 @@ type (
MyFloat64 float64
MyString string
MyBytes []byte
NamedStrings []MyString
NamedBytes []MyBytes
)
type ScalarProto2 struct {
Bool *bool `protobuf:"1"`
Int32 *int32 `protobuf:"2"`
Int64 *int64 `protobuf:"3"`
Uint32 *uint32 `protobuf:"4"`
Uint64 *uint64 `protobuf:"5"`
Float32 *float32 `protobuf:"6"`
Float64 *float64 `protobuf:"7"`
String *string `protobuf:"8"`
StringA []byte `protobuf:"9"`
Bytes []byte `protobuf:"10"`
BytesA *string `protobuf:"11"`
// List of test operations to perform on messages, vectors, or maps.
type (
messageOp interface{} // equalMessage | hasFields | getFields | setFields | clearFields | vectorFields | mapFields
messageOps []messageOp
MyBool *MyBool `protobuf:"12"`
MyInt32 *MyInt32 `protobuf:"13"`
MyInt64 *MyInt64 `protobuf:"14"`
MyUint32 *MyUint32 `protobuf:"15"`
MyUint64 *MyUint64 `protobuf:"16"`
MyFloat32 *MyFloat32 `protobuf:"17"`
MyFloat64 *MyFloat64 `protobuf:"18"`
MyString *MyString `protobuf:"19"`
MyStringA MyBytes `protobuf:"20"`
MyBytes MyBytes `protobuf:"21"`
MyBytesA *MyString `protobuf:"22"`
vectorOp interface{} // equalVector | lenVector | getVector | setVector | appendVector | truncVector
vectorOps []vectorOp
mapOp interface{} // TODO
mapOps []mapOp // TODO
)
// Test operations performed on a message.
type (
equalMessage pref.Message
hasFields map[pref.FieldNumber]bool
getFields map[pref.FieldNumber]pref.Value
setFields map[pref.FieldNumber]pref.Value
clearFields map[pref.FieldNumber]bool
vectorFields map[pref.FieldNumber]vectorOps
mapFields map[pref.FieldNumber]mapOps
messageFields map[pref.FieldNumber]messageOps
// TODO: Mutable, Range, ExtensionTypes
)
// Test operations performed on a vector.
type (
equalVector pref.Vector
lenVector int
getVector map[int]pref.Value
setVector map[int]pref.Value
appendVector []pref.Value
truncVector int
// TODO: Mutable, MutableAppend
)
func TestScalarProto2(t *testing.T) {
type ScalarProto2 struct {
Bool *bool `protobuf:"1"`
Int32 *int32 `protobuf:"2"`
Int64 *int64 `protobuf:"3"`
Uint32 *uint32 `protobuf:"4"`
Uint64 *uint64 `protobuf:"5"`
Float32 *float32 `protobuf:"6"`
Float64 *float64 `protobuf:"7"`
String *string `protobuf:"8"`
StringA []byte `protobuf:"9"`
Bytes []byte `protobuf:"10"`
BytesA *string `protobuf:"11"`
MyBool *MyBool `protobuf:"12"`
MyInt32 *MyInt32 `protobuf:"13"`
MyInt64 *MyInt64 `protobuf:"14"`
MyUint32 *MyUint32 `protobuf:"15"`
MyUint64 *MyUint64 `protobuf:"16"`
MyFloat32 *MyFloat32 `protobuf:"17"`
MyFloat64 *MyFloat64 `protobuf:"18"`
MyString *MyString `protobuf:"19"`
MyStringA MyBytes `protobuf:"20"`
MyBytes MyBytes `protobuf:"21"`
MyBytesA *MyString `protobuf:"22"`
}
mi := MessageType{Desc: mustMakeMessageDesc(ptype.StandaloneMessage{
Syntax: pref.Proto2,
FullName: "ScalarProto2",
Fields: []ptype.Field{
{Name: "f1", Number: 1, Cardinality: pref.Optional, Kind: pref.BoolKind, Default: V(bool(true))},
{Name: "f2", Number: 2, Cardinality: pref.Optional, Kind: pref.Int32Kind, Default: V(int32(2))},
{Name: "f3", Number: 3, Cardinality: pref.Optional, Kind: pref.Int64Kind, Default: V(int64(3))},
{Name: "f4", Number: 4, Cardinality: pref.Optional, Kind: pref.Uint32Kind, Default: V(uint32(4))},
{Name: "f5", Number: 5, Cardinality: pref.Optional, Kind: pref.Uint64Kind, Default: V(uint64(5))},
{Name: "f6", Number: 6, Cardinality: pref.Optional, Kind: pref.FloatKind, Default: V(float32(6))},
{Name: "f7", Number: 7, Cardinality: pref.Optional, Kind: pref.DoubleKind, Default: V(float64(7))},
{Name: "f8", Number: 8, Cardinality: pref.Optional, Kind: pref.StringKind, Default: V(string("8"))},
{Name: "f9", Number: 9, Cardinality: pref.Optional, Kind: pref.StringKind, Default: V(string("9"))},
{Name: "f10", Number: 10, Cardinality: pref.Optional, Kind: pref.BytesKind, Default: V([]byte("10"))},
{Name: "f11", Number: 11, Cardinality: pref.Optional, Kind: pref.BytesKind, Default: V([]byte("11"))},
{Name: "f12", Number: 12, Cardinality: pref.Optional, Kind: pref.BoolKind, Default: V(bool(true))},
{Name: "f13", Number: 13, Cardinality: pref.Optional, Kind: pref.Int32Kind, Default: V(int32(13))},
{Name: "f14", Number: 14, Cardinality: pref.Optional, Kind: pref.Int64Kind, Default: V(int64(14))},
{Name: "f15", Number: 15, Cardinality: pref.Optional, Kind: pref.Uint32Kind, Default: V(uint32(15))},
{Name: "f16", Number: 16, Cardinality: pref.Optional, Kind: pref.Uint64Kind, Default: V(uint64(16))},
{Name: "f17", Number: 17, Cardinality: pref.Optional, Kind: pref.FloatKind, Default: V(float32(17))},
{Name: "f18", Number: 18, Cardinality: pref.Optional, Kind: pref.DoubleKind, Default: V(float64(18))},
{Name: "f19", Number: 19, Cardinality: pref.Optional, Kind: pref.StringKind, Default: V(string("19"))},
{Name: "f20", Number: 20, Cardinality: pref.Optional, Kind: pref.StringKind, Default: V(string("20"))},
{Name: "f21", Number: 21, Cardinality: pref.Optional, Kind: pref.BytesKind, Default: V([]byte("21"))},
{Name: "f22", Number: 22, Cardinality: pref.Optional, Kind: pref.BytesKind, Default: V([]byte("22"))},
},
})}
testMessage(t, nil, mi.MessageOf(&ScalarProto2{}), messageOps{
hasFields{
1: false, 2: false, 3: false, 4: false, 5: false, 6: false, 7: false, 8: false, 9: false, 10: false, 11: false,
12: false, 13: false, 14: false, 15: false, 16: false, 17: false, 18: false, 19: false, 20: false, 21: false, 22: false,
},
getFields{
1: V(bool(true)), 2: V(int32(2)), 3: V(int64(3)), 4: V(uint32(4)), 5: V(uint64(5)), 6: V(float32(6)), 7: V(float64(7)), 8: V(string("8")), 9: V(string("9")), 10: V([]byte("10")), 11: V([]byte("11")),
12: V(bool(true)), 13: V(int32(13)), 14: V(int64(14)), 15: V(uint32(15)), 16: V(uint64(16)), 17: V(float32(17)), 18: V(float64(18)), 19: V(string("19")), 20: V(string("20")), 21: V([]byte("21")), 22: V([]byte("22")),
},
setFields{
1: V(bool(false)), 2: V(int32(0)), 3: V(int64(0)), 4: V(uint32(0)), 5: V(uint64(0)), 6: V(float32(0)), 7: V(float64(0)), 8: V(string("")), 9: V(string("")), 10: V([]byte(nil)), 11: V([]byte(nil)),
12: V(bool(false)), 13: V(int32(0)), 14: V(int64(0)), 15: V(uint32(0)), 16: V(uint64(0)), 17: V(float32(0)), 18: V(float64(0)), 19: V(string("")), 20: V(string("")), 21: V([]byte(nil)), 22: V([]byte(nil)),
},
hasFields{
1: true, 2: true, 3: true, 4: true, 5: true, 6: true, 7: true, 8: true, 9: true, 10: true, 11: true,
12: true, 13: true, 14: true, 15: true, 16: true, 17: true, 18: true, 19: true, 20: true, 21: true, 22: true,
},
equalMessage(mi.MessageOf(&ScalarProto2{
new(bool), new(int32), new(int64), new(uint32), new(uint64), new(float32), new(float64), new(string), []byte{}, []byte{}, new(string),
new(MyBool), new(MyInt32), new(MyInt64), new(MyUint32), new(MyUint64), new(MyFloat32), new(MyFloat64), new(MyString), MyBytes{}, MyBytes{}, new(MyString),
})),
clearFields{
1: true, 2: true, 3: true, 4: true, 5: true, 6: true, 7: true, 8: true, 9: true, 10: true, 11: true,
12: true, 13: true, 14: true, 15: true, 16: true, 17: true, 18: true, 19: true, 20: true, 21: true, 22: true,
},
equalMessage(mi.MessageOf(&ScalarProto2{})),
})
}
var scalarProto2Desc = mustMakeMessageDesc(ptype.StandaloneMessage{
Syntax: pref.Proto2,
FullName: "ScalarProto2",
Fields: []ptype.Field{
{Name: "f1", Number: 1, Cardinality: pref.Optional, Kind: pref.BoolKind, Default: pref.ValueOf(bool(true))},
{Name: "f2", Number: 2, Cardinality: pref.Optional, Kind: pref.Int32Kind, Default: pref.ValueOf(int32(2))},
{Name: "f3", Number: 3, Cardinality: pref.Optional, Kind: pref.Int64Kind, Default: pref.ValueOf(int64(3))},
{Name: "f4", Number: 4, Cardinality: pref.Optional, Kind: pref.Uint32Kind, Default: pref.ValueOf(uint32(4))},
{Name: "f5", Number: 5, Cardinality: pref.Optional, Kind: pref.Uint64Kind, Default: pref.ValueOf(uint64(5))},
{Name: "f6", Number: 6, Cardinality: pref.Optional, Kind: pref.FloatKind, Default: pref.ValueOf(float32(6))},
{Name: "f7", Number: 7, Cardinality: pref.Optional, Kind: pref.DoubleKind, Default: pref.ValueOf(float64(7))},
{Name: "f8", Number: 8, Cardinality: pref.Optional, Kind: pref.StringKind, Default: pref.ValueOf(string("8"))},
{Name: "f9", Number: 9, Cardinality: pref.Optional, Kind: pref.StringKind, Default: pref.ValueOf(string("9"))},
{Name: "f10", Number: 10, Cardinality: pref.Optional, Kind: pref.BytesKind, Default: pref.ValueOf([]byte("10"))},
{Name: "f11", Number: 11, Cardinality: pref.Optional, Kind: pref.BytesKind, Default: pref.ValueOf([]byte("11"))},
func TestScalarProto3(t *testing.T) {
type ScalarProto3 struct {
Bool bool `protobuf:"1"`
Int32 int32 `protobuf:"2"`
Int64 int64 `protobuf:"3"`
Uint32 uint32 `protobuf:"4"`
Uint64 uint64 `protobuf:"5"`
Float32 float32 `protobuf:"6"`
Float64 float64 `protobuf:"7"`
String string `protobuf:"8"`
StringA []byte `protobuf:"9"`
Bytes []byte `protobuf:"10"`
BytesA string `protobuf:"11"`
{Name: "f12", Number: 12, Cardinality: pref.Optional, Kind: pref.BoolKind, Default: pref.ValueOf(bool(true))},
{Name: "f13", Number: 13, Cardinality: pref.Optional, Kind: pref.Int32Kind, Default: pref.ValueOf(int32(13))},
{Name: "f14", Number: 14, Cardinality: pref.Optional, Kind: pref.Int64Kind, Default: pref.ValueOf(int64(14))},
{Name: "f15", Number: 15, Cardinality: pref.Optional, Kind: pref.Uint32Kind, Default: pref.ValueOf(uint32(15))},
{Name: "f16", Number: 16, Cardinality: pref.Optional, Kind: pref.Uint64Kind, Default: pref.ValueOf(uint64(16))},
{Name: "f17", Number: 17, Cardinality: pref.Optional, Kind: pref.FloatKind, Default: pref.ValueOf(float32(17))},
{Name: "f18", Number: 18, Cardinality: pref.Optional, Kind: pref.DoubleKind, Default: pref.ValueOf(float64(18))},
{Name: "f19", Number: 19, Cardinality: pref.Optional, Kind: pref.StringKind, Default: pref.ValueOf(string("19"))},
{Name: "f20", Number: 20, Cardinality: pref.Optional, Kind: pref.StringKind, Default: pref.ValueOf(string("20"))},
{Name: "f21", Number: 21, Cardinality: pref.Optional, Kind: pref.BytesKind, Default: pref.ValueOf([]byte("21"))},
{Name: "f22", Number: 22, Cardinality: pref.Optional, Kind: pref.BytesKind, Default: pref.ValueOf([]byte("22"))},
},
})
MyBool MyBool `protobuf:"12"`
MyInt32 MyInt32 `protobuf:"13"`
MyInt64 MyInt64 `protobuf:"14"`
MyUint32 MyUint32 `protobuf:"15"`
MyUint64 MyUint64 `protobuf:"16"`
MyFloat32 MyFloat32 `protobuf:"17"`
MyFloat64 MyFloat64 `protobuf:"18"`
MyString MyString `protobuf:"19"`
MyStringA MyBytes `protobuf:"20"`
MyBytes MyBytes `protobuf:"21"`
MyBytesA MyString `protobuf:"22"`
}
type ScalarProto3 struct {
Bool bool `protobuf:"1"`
Int32 int32 `protobuf:"2"`
Int64 int64 `protobuf:"3"`
Uint32 uint32 `protobuf:"4"`
Uint64 uint64 `protobuf:"5"`
Float32 float32 `protobuf:"6"`
Float64 float64 `protobuf:"7"`
String string `protobuf:"8"`
StringA []byte `protobuf:"9"`
Bytes []byte `protobuf:"10"`
BytesA string `protobuf:"11"`
mi := MessageType{Desc: mustMakeMessageDesc(ptype.StandaloneMessage{
Syntax: pref.Proto3,
FullName: "ScalarProto3",
Fields: []ptype.Field{
{Name: "f1", Number: 1, Cardinality: pref.Optional, Kind: pref.BoolKind},
{Name: "f2", Number: 2, Cardinality: pref.Optional, Kind: pref.Int32Kind},
{Name: "f3", Number: 3, Cardinality: pref.Optional, Kind: pref.Int64Kind},
{Name: "f4", Number: 4, Cardinality: pref.Optional, Kind: pref.Uint32Kind},
{Name: "f5", Number: 5, Cardinality: pref.Optional, Kind: pref.Uint64Kind},
{Name: "f6", Number: 6, Cardinality: pref.Optional, Kind: pref.FloatKind},
{Name: "f7", Number: 7, Cardinality: pref.Optional, Kind: pref.DoubleKind},
{Name: "f8", Number: 8, Cardinality: pref.Optional, Kind: pref.StringKind},
{Name: "f9", Number: 9, Cardinality: pref.Optional, Kind: pref.StringKind},
{Name: "f10", Number: 10, Cardinality: pref.Optional, Kind: pref.BytesKind},
{Name: "f11", Number: 11, Cardinality: pref.Optional, Kind: pref.BytesKind},
MyBool MyBool `protobuf:"12"`
MyInt32 MyInt32 `protobuf:"13"`
MyInt64 MyInt64 `protobuf:"14"`
MyUint32 MyUint32 `protobuf:"15"`
MyUint64 MyUint64 `protobuf:"16"`
MyFloat32 MyFloat32 `protobuf:"17"`
MyFloat64 MyFloat64 `protobuf:"18"`
MyString MyString `protobuf:"19"`
MyStringA MyBytes `protobuf:"20"`
MyBytes MyBytes `protobuf:"21"`
MyBytesA MyString `protobuf:"22"`
{Name: "f12", Number: 12, Cardinality: pref.Optional, Kind: pref.BoolKind},
{Name: "f13", Number: 13, Cardinality: pref.Optional, Kind: pref.Int32Kind},
{Name: "f14", Number: 14, Cardinality: pref.Optional, Kind: pref.Int64Kind},
{Name: "f15", Number: 15, Cardinality: pref.Optional, Kind: pref.Uint32Kind},
{Name: "f16", Number: 16, Cardinality: pref.Optional, Kind: pref.Uint64Kind},
{Name: "f17", Number: 17, Cardinality: pref.Optional, Kind: pref.FloatKind},
{Name: "f18", Number: 18, Cardinality: pref.Optional, Kind: pref.DoubleKind},
{Name: "f19", Number: 19, Cardinality: pref.Optional, Kind: pref.StringKind},
{Name: "f20", Number: 20, Cardinality: pref.Optional, Kind: pref.StringKind},
{Name: "f21", Number: 21, Cardinality: pref.Optional, Kind: pref.BytesKind},
{Name: "f22", Number: 22, Cardinality: pref.Optional, Kind: pref.BytesKind},
},
})}
testMessage(t, nil, mi.MessageOf(&ScalarProto3{}), messageOps{
hasFields{
1: false, 2: false, 3: false, 4: false, 5: false, 6: false, 7: false, 8: false, 9: false, 10: false, 11: false,
12: false, 13: false, 14: false, 15: false, 16: false, 17: false, 18: false, 19: false, 20: false, 21: false, 22: false,
},
getFields{
1: V(bool(false)), 2: V(int32(0)), 3: V(int64(0)), 4: V(uint32(0)), 5: V(uint64(0)), 6: V(float32(0)), 7: V(float64(0)), 8: V(string("")), 9: V(string("")), 10: V([]byte(nil)), 11: V([]byte(nil)),
12: V(bool(false)), 13: V(int32(0)), 14: V(int64(0)), 15: V(uint32(0)), 16: V(uint64(0)), 17: V(float32(0)), 18: V(float64(0)), 19: V(string("")), 20: V(string("")), 21: V([]byte(nil)), 22: V([]byte(nil)),
},
setFields{
1: V(bool(false)), 2: V(int32(0)), 3: V(int64(0)), 4: V(uint32(0)), 5: V(uint64(0)), 6: V(float32(0)), 7: V(float64(0)), 8: V(string("")), 9: V(string("")), 10: V([]byte(nil)), 11: V([]byte(nil)),
12: V(bool(false)), 13: V(int32(0)), 14: V(int64(0)), 15: V(uint32(0)), 16: V(uint64(0)), 17: V(float32(0)), 18: V(float64(0)), 19: V(string("")), 20: V(string("")), 21: V([]byte(nil)), 22: V([]byte(nil)),
},
hasFields{
1: false, 2: false, 3: false, 4: false, 5: false, 6: false, 7: false, 8: false, 9: false, 10: false, 11: false,
12: false, 13: false, 14: false, 15: false, 16: false, 17: false, 18: false, 19: false, 20: false, 21: false, 22: false,
},
equalMessage(mi.MessageOf(&ScalarProto3{})),
setFields{
1: V(bool(true)), 2: V(int32(2)), 3: V(int64(3)), 4: V(uint32(4)), 5: V(uint64(5)), 6: V(float32(6)), 7: V(float64(7)), 8: V(string("8")), 9: V(string("9")), 10: V([]byte("10")), 11: V([]byte("11")),
12: V(bool(true)), 13: V(int32(13)), 14: V(int64(14)), 15: V(uint32(15)), 16: V(uint64(16)), 17: V(float32(17)), 18: V(float64(18)), 19: V(string("19")), 20: V(string("20")), 21: V([]byte("21")), 22: V([]byte("22")),
},
hasFields{
1: true, 2: true, 3: true, 4: true, 5: true, 6: true, 7: true, 8: true, 9: true, 10: true, 11: true,
12: true, 13: true, 14: true, 15: true, 16: true, 17: true, 18: true, 19: true, 20: true, 21: true, 22: true,
},
equalMessage(mi.MessageOf(&ScalarProto3{
true, 2, 3, 4, 5, 6, 7, "8", []byte("9"), []byte("10"), "11",
true, 13, 14, 15, 16, 17, 18, "19", []byte("20"), []byte("21"), "22",
})),
clearFields{
1: true, 2: true, 3: true, 4: true, 5: true, 6: true, 7: true, 8: true, 9: true, 10: true, 11: true,
12: true, 13: true, 14: true, 15: true, 16: true, 17: true, 18: true, 19: true, 20: true, 21: true, 22: true,
},
equalMessage(mi.MessageOf(&ScalarProto3{})),
})
}
var scalarProto3Desc = mustMakeMessageDesc(ptype.StandaloneMessage{
Syntax: pref.Proto3,
FullName: "ScalarProto3",
Fields: []ptype.Field{
{Name: "f1", Number: 1, Cardinality: pref.Optional, Kind: pref.BoolKind},
{Name: "f2", Number: 2, Cardinality: pref.Optional, Kind: pref.Int32Kind},
{Name: "f3", Number: 3, Cardinality: pref.Optional, Kind: pref.Int64Kind},
{Name: "f4", Number: 4, Cardinality: pref.Optional, Kind: pref.Uint32Kind},
{Name: "f5", Number: 5, Cardinality: pref.Optional, Kind: pref.Uint64Kind},
{Name: "f6", Number: 6, Cardinality: pref.Optional, Kind: pref.FloatKind},
{Name: "f7", Number: 7, Cardinality: pref.Optional, Kind: pref.DoubleKind},
{Name: "f8", Number: 8, Cardinality: pref.Optional, Kind: pref.StringKind},
{Name: "f9", Number: 9, Cardinality: pref.Optional, Kind: pref.StringKind},
{Name: "f10", Number: 10, Cardinality: pref.Optional, Kind: pref.BytesKind},
{Name: "f11", Number: 11, Cardinality: pref.Optional, Kind: pref.BytesKind},
func TestRepeatedScalars(t *testing.T) {
type RepeatedScalars struct {
Bools []bool `protobuf:"1"`
Int32s []int32 `protobuf:"2"`
Int64s []int64 `protobuf:"3"`
Uint32s []uint32 `protobuf:"4"`
Uint64s []uint64 `protobuf:"5"`
Float32s []float32 `protobuf:"6"`
Float64s []float64 `protobuf:"7"`
Strings []string `protobuf:"8"`
StringsA [][]byte `protobuf:"9"`
Bytes [][]byte `protobuf:"10"`
BytesA []string `protobuf:"11"`
{Name: "f12", Number: 12, Cardinality: pref.Optional, Kind: pref.BoolKind},
{Name: "f13", Number: 13, Cardinality: pref.Optional, Kind: pref.Int32Kind},
{Name: "f14", Number: 14, Cardinality: pref.Optional, Kind: pref.Int64Kind},
{Name: "f15", Number: 15, Cardinality: pref.Optional, Kind: pref.Uint32Kind},
{Name: "f16", Number: 16, Cardinality: pref.Optional, Kind: pref.Uint64Kind},
{Name: "f17", Number: 17, Cardinality: pref.Optional, Kind: pref.FloatKind},
{Name: "f18", Number: 18, Cardinality: pref.Optional, Kind: pref.DoubleKind},
{Name: "f19", Number: 19, Cardinality: pref.Optional, Kind: pref.StringKind},
{Name: "f20", Number: 20, Cardinality: pref.Optional, Kind: pref.StringKind},
{Name: "f21", Number: 21, Cardinality: pref.Optional, Kind: pref.BytesKind},
{Name: "f22", Number: 22, Cardinality: pref.Optional, Kind: pref.BytesKind},
},
})
MyStrings1 []MyString `protobuf:"12"`
MyStrings2 []MyBytes `protobuf:"13"`
MyBytes1 []MyBytes `protobuf:"14"`
MyBytes2 []MyString `protobuf:"15"`
func TestKnownFields(t *testing.T) {
V := pref.ValueOf
type (
// has checks that each field matches the list.
hasOp []bool
// get checks that each field returns values matching the list.
getOp []pref.Value
// set calls set on each field with the given value in the list.
setOp []pref.Value
// clear calls clear on each field.
clearOp []bool
// equal checks that the current message equals the provided value.
equalOp struct{ want interface{} }
MyStrings3 NamedStrings `protobuf:"16"`
MyStrings4 NamedBytes `protobuf:"17"`
MyBytes3 NamedBytes `protobuf:"18"`
MyBytes4 NamedStrings `protobuf:"19"`
}
testOp interface{} // has | get | set | clear | equal
)
mi := MessageType{Desc: mustMakeMessageDesc(ptype.StandaloneMessage{
Syntax: pref.Proto2,
FullName: "RepeatedScalars",
Fields: []ptype.Field{
{Name: "f1", Number: 1, Cardinality: pref.Repeated, Kind: pref.BoolKind},
{Name: "f2", Number: 2, Cardinality: pref.Repeated, Kind: pref.Int32Kind},
{Name: "f3", Number: 3, Cardinality: pref.Repeated, Kind: pref.Int64Kind},
{Name: "f4", Number: 4, Cardinality: pref.Repeated, Kind: pref.Uint32Kind},
{Name: "f5", Number: 5, Cardinality: pref.Repeated, Kind: pref.Uint64Kind},
{Name: "f6", Number: 6, Cardinality: pref.Repeated, Kind: pref.FloatKind},
{Name: "f7", Number: 7, Cardinality: pref.Repeated, Kind: pref.DoubleKind},
{Name: "f8", Number: 8, Cardinality: pref.Repeated, Kind: pref.StringKind},
{Name: "f9", Number: 9, Cardinality: pref.Repeated, Kind: pref.StringKind},
{Name: "f10", Number: 10, Cardinality: pref.Repeated, Kind: pref.BytesKind},
{Name: "f11", Number: 11, Cardinality: pref.Repeated, Kind: pref.BytesKind},
tests := []struct {
structType reflect.Type
messageDesc pref.MessageDescriptor
testOps []testOp
}{{
structType: reflect.TypeOf(ScalarProto2{}),
messageDesc: scalarProto2Desc,
testOps: []testOp{
hasOp([]bool{
false, false, false, false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false, false, false, false,
}),
getOp([]pref.Value{
V(bool(true)), V(int32(2)), V(int64(3)), V(uint32(4)), V(uint64(5)), V(float32(6)), V(float64(7)), V(string("8")), V(string("9")), V([]byte("10")), V([]byte("11")),
V(bool(true)), V(int32(13)), V(int64(14)), V(uint32(15)), V(uint64(16)), V(float32(17)), V(float64(18)), V(string("19")), V(string("20")), V([]byte("21")), V([]byte("22")),
}),
setOp([]pref.Value{
V(bool(false)), V(int32(0)), V(int64(0)), V(uint32(0)), V(uint64(0)), V(float32(0)), V(float64(0)), V(string("")), V(string("")), V([]byte(nil)), V([]byte(nil)),
V(bool(false)), V(int32(0)), V(int64(0)), V(uint32(0)), V(uint64(0)), V(float32(0)), V(float64(0)), V(string("")), V(string("")), V([]byte(nil)), V([]byte(nil)),
}),
hasOp([]bool{
true, true, true, true, true, true, true, true, true, true, true,
true, true, true, true, true, true, true, true, true, true, true,
}),
equalOp{&ScalarProto2{
new(bool), new(int32), new(int64), new(uint32), new(uint64), new(float32), new(float64), new(string), []byte{}, []byte{}, new(string),
new(MyBool), new(MyInt32), new(MyInt64), new(MyUint32), new(MyUint64), new(MyFloat32), new(MyFloat64), new(MyString), MyBytes{}, MyBytes{}, new(MyString),
}},
clearOp([]bool{
true, true, true, true, true, true, true, true, true, true, true,
true, true, true, true, true, true, true, true, true, true, true,
}),
equalOp{&ScalarProto2{}},
{Name: "f12", Number: 12, Cardinality: pref.Repeated, Kind: pref.StringKind},
{Name: "f13", Number: 13, Cardinality: pref.Repeated, Kind: pref.StringKind},
{Name: "f14", Number: 14, Cardinality: pref.Repeated, Kind: pref.BytesKind},
{Name: "f15", Number: 15, Cardinality: pref.Repeated, Kind: pref.BytesKind},
{Name: "f16", Number: 16, Cardinality: pref.Repeated, Kind: pref.StringKind},
{Name: "f17", Number: 17, Cardinality: pref.Repeated, Kind: pref.StringKind},
{Name: "f18", Number: 18, Cardinality: pref.Repeated, Kind: pref.BytesKind},
{Name: "f19", Number: 19, Cardinality: pref.Repeated, Kind: pref.BytesKind},
},
}, {
structType: reflect.TypeOf(ScalarProto3{}),
messageDesc: scalarProto3Desc,
testOps: []testOp{
hasOp([]bool{
false, false, false, false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false, false, false, false,
}),
getOp([]pref.Value{
V(bool(false)), V(int32(0)), V(int64(0)), V(uint32(0)), V(uint64(0)), V(float32(0)), V(float64(0)), V(string("")), V(string("")), V([]byte(nil)), V([]byte(nil)),
V(bool(false)), V(int32(0)), V(int64(0)), V(uint32(0)), V(uint64(0)), V(float32(0)), V(float64(0)), V(string("")), V(string("")), V([]byte(nil)), V([]byte(nil)),
}),
setOp([]pref.Value{
V(bool(false)), V(int32(0)), V(int64(0)), V(uint32(0)), V(uint64(0)), V(float32(0)), V(float64(0)), V(string("")), V(string("")), V([]byte(nil)), V([]byte(nil)),
V(bool(false)), V(int32(0)), V(int64(0)), V(uint32(0)), V(uint64(0)), V(float32(0)), V(float64(0)), V(string("")), V(string("")), V([]byte(nil)), V([]byte(nil)),
}),
hasOp([]bool{
false, false, false, false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false, false, false, false,
}),
equalOp{&ScalarProto3{}},
setOp([]pref.Value{
V(bool(true)), V(int32(2)), V(int64(3)), V(uint32(4)), V(uint64(5)), V(float32(6)), V(float64(7)), V(string("8")), V(string("9")), V([]byte("10")), V([]byte("11")),
V(bool(true)), V(int32(13)), V(int64(14)), V(uint32(15)), V(uint64(16)), V(float32(17)), V(float64(18)), V(string("19")), V(string("20")), V([]byte("21")), V([]byte("22")),
}),
hasOp([]bool{
true, true, true, true, true, true, true, true, true, true, true,
true, true, true, true, true, true, true, true, true, true, true,
}),
equalOp{&ScalarProto3{
true, 2, 3, 4, 5, 6, 7, "8", []byte("9"), []byte("10"), "11",
true, 13, 14, 15, 16, 17, 18, "19", []byte("20"), []byte("21"), "22",
}},
clearOp([]bool{
true, true, true, true, true, true, true, true, true, true, true,
true, true, true, true, true, true, true, true, true, true, true,
}),
equalOp{&ScalarProto3{}},
})}
empty := mi.MessageOf(&RepeatedScalars{})
emptyFS := empty.KnownFields()
want := mi.MessageOf(&RepeatedScalars{
Bools: []bool{true, false, true},
Int32s: []int32{2, math.MinInt32, math.MaxInt32},
Int64s: []int64{3, math.MinInt64, math.MaxInt64},
Uint32s: []uint32{4, math.MaxUint32 / 2, math.MaxUint32},
Uint64s: []uint64{5, math.MaxUint64 / 2, math.MaxUint64},
Float32s: []float32{6, math.SmallestNonzeroFloat32, float32(math.NaN()), math.MaxFloat32},
Float64s: []float64{7, math.SmallestNonzeroFloat64, float64(math.NaN()), math.MaxFloat64},
Strings: []string{"8", "", "eight"},
StringsA: [][]byte{[]byte("9"), nil, []byte("nine")},
Bytes: [][]byte{[]byte("10"), nil, []byte("ten")},
BytesA: []string{"11", "", "eleven"},
MyStrings1: []MyString{"12", "", "twelve"},
MyStrings2: []MyBytes{[]byte("13"), nil, []byte("thirteen")},
MyBytes1: []MyBytes{[]byte("14"), nil, []byte("fourteen")},
MyBytes2: []MyString{"15", "", "fifteen"},
MyStrings3: NamedStrings{"16", "", "sixteen"},
MyStrings4: NamedBytes{[]byte("17"), nil, []byte("seventeen")},
MyBytes3: NamedBytes{[]byte("18"), nil, []byte("eighteen")},
MyBytes4: NamedStrings{"19", "", "nineteen"},
})
wantFS := want.KnownFields()
testMessage(t, nil, mi.MessageOf(&RepeatedScalars{}), messageOps{
hasFields{1: false, 2: false, 3: false, 4: false, 5: false, 6: false, 7: false, 8: false, 9: false, 10: false, 11: false, 12: false, 13: false, 14: false, 15: false, 16: false, 17: false, 18: false, 19: false},
getFields{1: emptyFS.Get(1), 3: emptyFS.Get(3), 5: emptyFS.Get(5), 7: emptyFS.Get(7), 9: emptyFS.Get(9), 11: emptyFS.Get(11), 13: emptyFS.Get(13), 15: emptyFS.Get(15), 17: emptyFS.Get(17), 19: emptyFS.Get(19)},
setFields{1: wantFS.Get(1), 3: wantFS.Get(3), 5: wantFS.Get(5), 7: wantFS.Get(7), 9: wantFS.Get(9), 11: wantFS.Get(11), 13: wantFS.Get(13), 15: wantFS.Get(15), 17: wantFS.Get(17), 19: wantFS.Get(19)},
vectorFields{
2: {
lenVector(0),
appendVector{V(int32(2)), V(int32(math.MinInt32)), V(int32(math.MaxInt32))},
getVector{0: V(int32(2)), 1: V(int32(math.MinInt32)), 2: V(int32(math.MaxInt32))},
equalVector(wantFS.Get(2).Vector()),
},
4: {
appendVector{V(uint32(0)), V(uint32(0)), V(uint32(0))},
setVector{0: V(uint32(4)), 1: V(uint32(math.MaxUint32 / 2)), 2: V(uint32(math.MaxUint32))},
lenVector(3),
},
6: {
appendVector{V(float32(6)), V(float32(math.SmallestNonzeroFloat32)), V(float32(math.NaN())), V(float32(math.MaxFloat32))},
equalVector(wantFS.Get(6).Vector()),
},
8: {
appendVector{V(""), V(""), V(""), V(""), V(""), V("")},
lenVector(6),
setVector{0: V("8"), 2: V("eight")},
truncVector(3),
equalVector(wantFS.Get(8).Vector()),
},
10: {
appendVector{V([]byte(nil)), V([]byte(nil))},
setVector{0: V([]byte("10"))},
appendVector{V([]byte("wrong"))},
setVector{2: V([]byte("ten"))},
equalVector(wantFS.Get(10).Vector()),
},
12: {
appendVector{V("12"), V("wrong"), V("twelve")},
setVector{1: V("")},
equalVector(wantFS.Get(12).Vector()),
},
14: {
appendVector{V([]byte("14")), V([]byte(nil)), V([]byte("fourteen"))},
equalVector(wantFS.Get(14).Vector()),
},
16: {
appendVector{V("16"), V(""), V("sixteen"), V("extra")},
truncVector(3),
equalVector(wantFS.Get(16).Vector()),
},
18: {
appendVector{V([]byte("18")), V([]byte(nil)), V([]byte("eighteen"))},
equalVector(wantFS.Get(18).Vector()),
},
},
}}
hasFields{1: true, 2: true, 3: true, 4: true, 5: true, 6: true, 7: true, 8: true, 9: true, 10: true, 11: true, 12: true, 13: true, 14: true, 15: true, 16: true, 17: true, 18: true, 19: true},
equalMessage(want),
clearFields{1: true, 2: true, 3: true, 4: true, 5: true, 6: true, 7: true, 8: true, 9: true, 10: true, 11: true, 12: true, 13: true, 14: true, 15: true, 16: true, 17: true, 18: true, 19: true},
equalMessage(mi.MessageOf(&RepeatedScalars{})),
})
}
for _, tt := range tests {
t.Run(tt.structType.Name(), func(t *testing.T) {
mi := MessageType{Desc: tt.messageDesc}
// TODO: Need to test singular and repeated messages
// Test the field functions.
p := reflect.New(tt.structType).Interface()
m := mi.MessageOf(p)
fs := m.KnownFields()
for i, op := range tt.testOps {
switch op := op.(type) {
case hasOp:
got := map[pref.FieldNumber]bool{}
want := map[pref.FieldNumber]bool{}
for j, ok := range op {
n := pref.FieldNumber(j + 1)
got[n] = fs.Has(n)
want[n] = ok
}
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("operation %d, has mismatch (-want, +got):\n%s", i, diff)
}
case getOp:
got := map[pref.FieldNumber]pref.Value{}
want := map[pref.FieldNumber]pref.Value{}
for j, v := range op {
n := pref.FieldNumber(j + 1)
got[n] = fs.Get(n)
want[n] = v
}
xformValue := cmp.Transformer("", func(v pref.Value) interface{} {
return v.Interface()
})
if diff := cmp.Diff(want, got, xformValue); diff != "" {
t.Errorf("operation %d, get mismatch (-want, +got):\n%s", i, diff)
}
case setOp:
for j, v := range op {
n := pref.FieldNumber(j + 1)
fs.Set(n, v)
}
case clearOp:
for j, ok := range op {
n := pref.FieldNumber(j + 1)
if ok {
fs.Clear(n)
}
}
case equalOp:
got := m.(interface{ Unwrap() interface{} }).Unwrap()
if diff := cmp.Diff(op.want, got); diff != "" {
t.Errorf("operation %d, equal mismatch (-want, +got):\n%s", i, diff)
}
var cmpOpts = cmp.Options{
cmp.Transformer("UnwrapValue", func(v pref.Value) interface{} {
return v.Interface()
}),
cmp.Transformer("UnwrapMessage", func(m pref.Message) interface{} {
v := m.Interface()
if v, ok := v.(interface{ Unwrap() interface{} }); ok {
return v.Unwrap()
}
return v
}),
cmp.Transformer("UnwrapVector", func(v pref.Vector) interface{} {
return v.(interface{ Unwrap() interface{} }).Unwrap()
}),
cmp.Transformer("UnwrapMap", func(m pref.Map) interface{} {
return m.(interface{ Unwrap() interface{} }).Unwrap()
}),
cmpopts.EquateNaNs(),
}
func testMessage(t *testing.T, p path, m pref.Message, tt messageOps) {
fs := m.KnownFields()
for i, op := range tt {
p.Push(i)
switch op := op.(type) {
case equalMessage:
if diff := cmp.Diff(op, m, cmpOpts); diff != "" {
t.Errorf("operation %v, message mismatch (-want, +got):\n%s", p, diff)
}
case hasFields:
got := map[pref.FieldNumber]bool{}
want := map[pref.FieldNumber]bool(op)
for n := range want {
got[n] = fs.Has(n)
}
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("operation %v, KnownFields.Has mismatch (-want, +got):\n%s", p, diff)
}
case getFields:
got := map[pref.FieldNumber]pref.Value{}
want := map[pref.FieldNumber]pref.Value(op)
for n := range want {
got[n] = fs.Get(n)
}
if diff := cmp.Diff(want, got, cmpOpts); diff != "" {
t.Errorf("operation %v, KnownFields.Get mismatch (-want, +got):\n%s", p, diff)
}
case setFields:
for n, v := range op {
fs.Set(n, v)
}
case clearFields:
for n, ok := range op {
if ok {
fs.Clear(n)
}
}
})
case vectorFields:
for n, tt := range op {
p.Push(int(n))
testVectors(t, p, fs.Mutable(n).(pref.Vector), tt)
p.Pop()
}
default:
t.Fatalf("operation %v, invalid operation: %T", p, op)
}
p.Pop()
}
}
func testVectors(t *testing.T, p path, v pref.Vector, tt vectorOps) {
for i, op := range tt {
p.Push(i)
switch op := op.(type) {
case equalVector:
if diff := cmp.Diff(op, v, cmpOpts); diff != "" {
t.Errorf("operation %v, vector mismatch (-want, +got):\n%s", p, diff)
}
case lenVector:
if got, want := v.Len(), int(op); got != want {
t.Errorf("operation %v, Vector.Len = %d, want %d", p, got, want)
}
case getVector:
got := map[int]pref.Value{}
want := map[int]pref.Value(op)
for n := range want {
got[n] = v.Get(n)
}
if diff := cmp.Diff(want, got, cmpOpts); diff != "" {
t.Errorf("operation %v, Vector.Get mismatch (-want, +got):\n%s", p, diff)
}
case setVector:
for n, e := range op {
v.Set(n, e)
}
case appendVector:
for _, e := range op {
v.Append(e)
}
case truncVector:
v.Truncate(int(op))
default:
t.Fatalf("operation %v, invalid operation: %T", p, op)
}
p.Pop()
}
}
type path []int
func (p *path) Push(i int) { *p = append(*p, i) }
func (p *path) Pop() { *p = (*p)[:len(*p)-1] }
func (p path) String() string {
var ss []string
for _, i := range p {
ss = append(ss, fmt.Sprint(i))
}
return strings.Join(ss, ".")
}