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,9 +41,78 @@ 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{})
@ -223,6 +292,7 @@ func matchGoTypePBKind(t reflect.Type, k pref.Kind) converter {
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,8 +37,48 @@ type (
MyFloat64 float64 MyFloat64 float64
MyString string MyString string
MyBytes []byte MyBytes []byte
NamedStrings []MyString
NamedBytes []MyBytes
) )
// List of test operations to perform on messages, vectors, or maps.
type (
messageOp interface{} // equalMessage | hasFields | getFields | setFields | clearFields | vectorFields | mapFields
messageOps []messageOp
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 { type ScalarProto2 struct {
Bool *bool `protobuf:"1"` Bool *bool `protobuf:"1"`
Int32 *int32 `protobuf:"2"` Int32 *int32 `protobuf:"2"`
@ -60,36 +105,66 @@ type ScalarProto2 struct {
MyBytesA *MyString `protobuf:"22"` MyBytesA *MyString `protobuf:"22"`
} }
var scalarProto2Desc = mustMakeMessageDesc(ptype.StandaloneMessage{ mi := MessageType{Desc: mustMakeMessageDesc(ptype.StandaloneMessage{
Syntax: pref.Proto2, Syntax: pref.Proto2,
FullName: "ScalarProto2", FullName: "ScalarProto2",
Fields: []ptype.Field{ Fields: []ptype.Field{
{Name: "f1", Number: 1, Cardinality: pref.Optional, Kind: pref.BoolKind, Default: pref.ValueOf(bool(true))}, {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: pref.ValueOf(int32(2))}, {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: pref.ValueOf(int64(3))}, {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: pref.ValueOf(uint32(4))}, {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: pref.ValueOf(uint64(5))}, {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: pref.ValueOf(float32(6))}, {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: pref.ValueOf(float64(7))}, {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: pref.ValueOf(string("8"))}, {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: pref.ValueOf(string("9"))}, {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: pref.ValueOf([]byte("10"))}, {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: pref.ValueOf([]byte("11"))}, {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: pref.ValueOf(bool(true))}, {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: pref.ValueOf(int32(13))}, {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: pref.ValueOf(int64(14))}, {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: pref.ValueOf(uint32(15))}, {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: pref.ValueOf(uint64(16))}, {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: pref.ValueOf(float32(17))}, {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: pref.ValueOf(float64(18))}, {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: pref.ValueOf(string("19"))}, {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: pref.ValueOf(string("20"))}, {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: pref.ValueOf([]byte("21"))}, {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: pref.ValueOf([]byte("22"))}, {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{})),
})
}
func TestScalarProto3(t *testing.T) {
type ScalarProto3 struct { type ScalarProto3 struct {
Bool bool `protobuf:"1"` Bool bool `protobuf:"1"`
Int32 int32 `protobuf:"2"` Int32 int32 `protobuf:"2"`
@ -116,7 +191,7 @@ type ScalarProto3 struct {
MyBytesA MyString `protobuf:"22"` MyBytesA MyString `protobuf:"22"`
} }
var scalarProto3Desc = mustMakeMessageDesc(ptype.StandaloneMessage{ mi := MessageType{Desc: mustMakeMessageDesc(ptype.StandaloneMessage{
Syntax: pref.Proto3, Syntax: pref.Proto3,
FullName: "ScalarProto3", FullName: "ScalarProto3",
Fields: []ptype.Field{ Fields: []ptype.Field{
@ -144,154 +219,305 @@ var scalarProto3Desc = mustMakeMessageDesc(ptype.StandaloneMessage{
{Name: "f21", Number: 21, Cardinality: pref.Optional, Kind: pref.BytesKind}, {Name: "f21", Number: 21, Cardinality: pref.Optional, Kind: pref.BytesKind},
{Name: "f22", Number: 22, Cardinality: pref.Optional, Kind: pref.BytesKind}, {Name: "f22", Number: 22, Cardinality: pref.Optional, Kind: pref.BytesKind},
}, },
}) })}
func TestKnownFields(t *testing.T) { testMessage(t, nil, mi.MessageOf(&ScalarProto3{}), messageOps{
V := pref.ValueOf hasFields{
type ( 1: false, 2: false, 3: false, 4: false, 5: false, 6: false, 7: false, 8: false, 9: false, 10: false, 11: false,
// has checks that each field matches the list. 12: false, 13: false, 14: false, 15: false, 16: false, 17: false, 18: false, 19: false, 20: false, 21: false, 22: false,
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
)
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{}},
}, },
}, { getFields{
structType: reflect.TypeOf(ScalarProto3{}), 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)),
messageDesc: scalarProto3Desc, 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)),
testOps: []testOp{ },
hasOp([]bool{ setFields{
false, false, false, false, false, false, false, false, false, false, false, 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)),
false, false, false, false, false, false, false, false, false, false, false, 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)),
}), },
getOp([]pref.Value{ hasFields{
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)), 1: false, 2: false, 3: false, 4: false, 5: false, 6: false, 7: false, 8: false, 9: false, 10: false, 11: false,
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)), 12: false, 13: false, 14: false, 15: false, 16: false, 17: false, 18: false, 19: false, 20: false, 21: false, 22: false,
}), },
setOp([]pref.Value{ equalMessage(mi.MessageOf(&ScalarProto3{})),
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)), setFields{
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)), 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")),
hasOp([]bool{ },
false, false, false, false, false, false, false, false, false, false, false, hasFields{
false, false, false, false, false, false, false, false, false, false, false, 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,
equalOp{&ScalarProto3{}}, },
setOp([]pref.Value{ equalMessage(mi.MessageOf(&ScalarProto3{
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, 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", true, 13, 14, 15, 16, 17, 18, "19", []byte("20"), []byte("21"), "22",
}}, })),
clearOp([]bool{ clearFields{
true, true, true, true, true, true, true, true, true, true, true, 1: true, 2: true, 3: true, 4: true, 5: true, 6: true, 7: true, 8: true, 9: true, 10: true, 11: true,
true, true, true, true, true, true, true, true, true, true, true, 12: true, 13: true, 14: true, 15: true, 16: true, 17: true, 18: true, 19: true, 20: true, 21: true, 22: true,
}),
equalOp{&ScalarProto3{}},
}, },
}} equalMessage(mi.MessageOf(&ScalarProto3{})),
})
}
for _, tt := range tests { func TestRepeatedScalars(t *testing.T) {
t.Run(tt.structType.Name(), func(t *testing.T) { type RepeatedScalars struct {
mi := MessageType{Desc: tt.messageDesc} 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"`
// Test the field functions. MyStrings1 []MyString `protobuf:"12"`
p := reflect.New(tt.structType).Interface() MyStrings2 []MyBytes `protobuf:"13"`
m := mi.MessageOf(p) MyBytes1 []MyBytes `protobuf:"14"`
MyBytes2 []MyString `protobuf:"15"`
MyStrings3 NamedStrings `protobuf:"16"`
MyStrings4 NamedBytes `protobuf:"17"`
MyBytes3 NamedBytes `protobuf:"18"`
MyBytes4 NamedStrings `protobuf:"19"`
}
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},
{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},
},
})}
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{})),
})
}
// TODO: Need to test singular and repeated messages
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() fs := m.KnownFields()
for i, op := range tt.testOps { for i, op := range tt {
p.Push(i)
switch op := op.(type) { switch op := op.(type) {
case hasOp: 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{} got := map[pref.FieldNumber]bool{}
want := map[pref.FieldNumber]bool{} want := map[pref.FieldNumber]bool(op)
for j, ok := range op { for n := range want {
n := pref.FieldNumber(j + 1)
got[n] = fs.Has(n) got[n] = fs.Has(n)
want[n] = ok
} }
if diff := cmp.Diff(want, got); diff != "" { if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("operation %d, has mismatch (-want, +got):\n%s", i, diff) t.Errorf("operation %v, KnownFields.Has mismatch (-want, +got):\n%s", p, diff)
} }
case getOp: case getFields:
got := map[pref.FieldNumber]pref.Value{} got := map[pref.FieldNumber]pref.Value{}
want := map[pref.FieldNumber]pref.Value{} want := map[pref.FieldNumber]pref.Value(op)
for j, v := range op { for n := range want {
n := pref.FieldNumber(j + 1)
got[n] = fs.Get(n) got[n] = fs.Get(n)
want[n] = v
} }
xformValue := cmp.Transformer("", func(v pref.Value) interface{} { if diff := cmp.Diff(want, got, cmpOpts); diff != "" {
return v.Interface() t.Errorf("operation %v, KnownFields.Get 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 setOp: case setFields:
for j, v := range op { for n, v := range op {
n := pref.FieldNumber(j + 1)
fs.Set(n, v) fs.Set(n, v)
} }
case clearOp: case clearFields:
for j, ok := range op { for n, ok := range op {
n := pref.FieldNumber(j + 1)
if ok { if ok {
fs.Clear(n) fs.Clear(n)
} }
} }
case equalOp: case vectorFields:
got := m.(interface{ Unwrap() interface{} }).Unwrap() for n, tt := range op {
if diff := cmp.Diff(op.want, got); diff != "" { p.Push(int(n))
t.Errorf("operation %d, equal mismatch (-want, +got):\n%s", i, diff) 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, ".")
}