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 { func (m *message) UnknownFields() pref.UnknownFields {
return (*unknownFields)(m) return (*unknownFields)(m)
} }
func (m *message) Unwrap() interface{} { func (m *message) Unwrap() interface{} { // TODO: unexport?
return m.p.asType(m.mi.goType.Elem()).Interface() return m.p.asType(m.mi.goType.Elem()).Interface()
} }
func (m *message) Interface() pref.ProtoMessage { func (m *message) Interface() pref.ProtoMessage {
@ -266,7 +266,7 @@ func (fs *knownFields) Set(n pref.FieldNumber, v pref.Value) {
return return
} }
// TODO: Handle extension fields. // TODO: Handle extension fields.
panic("invalid field") panic(fmt.Sprintf("invalid field: %d", n))
} }
func (fs *knownFields) Clear(n pref.FieldNumber) { func (fs *knownFields) Clear(n pref.FieldNumber) {
if fi := fs.mi.fields[n]; fi != nil { if fi := fs.mi.fields[n]; fi != nil {
@ -274,14 +274,14 @@ func (fs *knownFields) Clear(n pref.FieldNumber) {
return return
} }
// TODO: Handle extension fields. // TODO: Handle extension fields.
panic("invalid field") panic(fmt.Sprintf("invalid field: %d", n))
} }
func (fs *knownFields) Mutable(n pref.FieldNumber) pref.Mutable { func (fs *knownFields) Mutable(n pref.FieldNumber) pref.Mutable {
if fi := fs.mi.fields[n]; fi != nil { if fi := fs.mi.fields[n]; fi != nil {
return fi.mutable(fs.p) return fi.mutable(fs.p)
} }
// TODO: Handle extension fields. // 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) { func (fs *knownFields) Range(f func(pref.FieldNumber, pref.Value) bool) {
for n, fi := range fs.mi.fields { 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 { func fieldInfoForVector(fd pref.FieldDescriptor, fs reflect.StructField) fieldInfo {
// TODO: support vector fields. ft := fs.Type
panic(fmt.Sprintf("invalid field: %v", fd)) 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{}) var emptyBytes = reflect.ValueOf([]byte{})
func fieldInfoForScalar(fd pref.FieldDescriptor, fs reflect.StructField) fieldInfo { 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 // converter provides functions for converting to/from Go reflect.Value types
// and protobuf protoreflect.Value types. // and protobuf protoreflect.Value types.
type converter struct { type converter struct {
toPB func(reflect.Value) pref.Value toPB func(reflect.Value) pref.Value
toGo func(pref.Value) reflect.Value toGo func(pref.Value) reflect.Value
newMessage func() pref.Message
} }
func makeScalarConverter(goType, pbType reflect.Type) converter { func makeScalarConverter(goType, pbType reflect.Type) converter {

View File

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