mirror of
https://github.com/protocolbuffers/protobuf-go.git
synced 2024-12-26 03:20:53 +00:00
eb7b468655
For golang/protobuf#1657 Change-Id: I7b2b0c30506706015ce278e6054439c9ad9ef727 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/634815 TryBot-Bypass: Michael Stapelberg <stapelberg@google.com> Reviewed-by: Joseph Tsai <joetsai@digital-static.net> Reviewed-by: Damien Neil <dneil@google.com>
561 lines
15 KiB
Go
561 lines
15 KiB
Go
// Copyright 2024 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package proto_test
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"testing"
|
|
"unsafe"
|
|
|
|
"google.golang.org/protobuf/internal/impl"
|
|
testhybridpb "google.golang.org/protobuf/internal/testprotos/testeditions/testeditions_hybrid"
|
|
testopaquepb "google.golang.org/protobuf/internal/testprotos/testeditions/testeditions_opaque"
|
|
"google.golang.org/protobuf/proto"
|
|
)
|
|
|
|
func TestOpenSetRepeatedNilReceiver(t *testing.T) {
|
|
var x *testhybridpb.TestAllTypes
|
|
expectPanic(t, func() {
|
|
x.SetRepeatedUint32(nil)
|
|
}, "Setting repeated field on nil receiver did not panic.")
|
|
}
|
|
|
|
func TestOpenSetRepeated(t *testing.T) {
|
|
x := &testhybridpb.TestAllTypes{}
|
|
|
|
tab := []struct {
|
|
fName string // Field name (in proto)
|
|
set func() // Set the field to empty slice
|
|
setNil func() // Set the field to nil
|
|
len func() int // length of field, -1 if nil
|
|
}{
|
|
{
|
|
fName: "repeated_int32",
|
|
set: func() { x.SetRepeatedInt32([]int32{}) },
|
|
setNil: func() { x.SetRepeatedInt32(nil) },
|
|
len: func() int {
|
|
if x.GetRepeatedInt32() == nil {
|
|
return -1
|
|
}
|
|
return len(x.GetRepeatedInt32())
|
|
},
|
|
},
|
|
{
|
|
fName: "repeated_int64",
|
|
set: func() { x.SetRepeatedInt64([]int64{}) },
|
|
setNil: func() { x.SetRepeatedInt64(nil) },
|
|
len: func() int {
|
|
if x.GetRepeatedInt64() == nil {
|
|
return -1
|
|
}
|
|
return len(x.GetRepeatedInt64())
|
|
},
|
|
},
|
|
{
|
|
fName: "repeated_uint32",
|
|
set: func() { x.SetRepeatedUint32([]uint32{}) },
|
|
setNil: func() { x.SetRepeatedUint32(nil) },
|
|
len: func() int {
|
|
if x.GetRepeatedUint32() == nil {
|
|
return -1
|
|
}
|
|
return len(x.GetRepeatedUint32())
|
|
},
|
|
},
|
|
{
|
|
fName: "repeated_uint64",
|
|
set: func() { x.SetRepeatedUint64([]uint64{}) },
|
|
setNil: func() { x.SetRepeatedUint64(nil) },
|
|
len: func() int {
|
|
if x.GetRepeatedUint64() == nil {
|
|
return -1
|
|
}
|
|
return len(x.GetRepeatedUint64())
|
|
},
|
|
},
|
|
{
|
|
fName: "repeated_sint32",
|
|
set: func() { x.SetRepeatedSint32([]int32{}) },
|
|
setNil: func() { x.SetRepeatedSint32(nil) },
|
|
len: func() int {
|
|
if x.GetRepeatedSint32() == nil {
|
|
return -1
|
|
}
|
|
return len(x.GetRepeatedSint32())
|
|
},
|
|
},
|
|
{
|
|
fName: "repeated_sint64",
|
|
set: func() { x.SetRepeatedSint64([]int64{}) },
|
|
setNil: func() { x.SetRepeatedSint64(nil) },
|
|
len: func() int {
|
|
if x.GetRepeatedSint64() == nil {
|
|
return -1
|
|
}
|
|
return len(x.GetRepeatedSint64())
|
|
},
|
|
},
|
|
{
|
|
fName: "repeated_fixed32",
|
|
set: func() { x.SetRepeatedFixed32([]uint32{}) },
|
|
setNil: func() { x.SetRepeatedFixed32(nil) },
|
|
len: func() int {
|
|
if x.GetRepeatedFixed32() == nil {
|
|
return -1
|
|
}
|
|
return len(x.GetRepeatedFixed32())
|
|
},
|
|
},
|
|
{
|
|
fName: "repeated_fixed64",
|
|
set: func() { x.SetRepeatedFixed64([]uint64{}) },
|
|
setNil: func() { x.SetRepeatedFixed64(nil) },
|
|
len: func() int {
|
|
if x.GetRepeatedFixed64() == nil {
|
|
return -1
|
|
}
|
|
return len(x.GetRepeatedFixed64())
|
|
},
|
|
},
|
|
{
|
|
fName: "repeated_sfixed32",
|
|
set: func() { x.SetRepeatedSfixed32([]int32{}) },
|
|
setNil: func() { x.SetRepeatedSfixed32(nil) },
|
|
len: func() int {
|
|
if x.GetRepeatedSfixed32() == nil {
|
|
return -1
|
|
}
|
|
return len(x.GetRepeatedSfixed32())
|
|
},
|
|
},
|
|
{
|
|
fName: "repeated_sfixed64",
|
|
set: func() { x.SetRepeatedSfixed64([]int64{}) },
|
|
setNil: func() { x.SetRepeatedSfixed64(nil) },
|
|
len: func() int {
|
|
if x.GetRepeatedSfixed64() == nil {
|
|
return -1
|
|
}
|
|
return len(x.GetRepeatedSfixed64())
|
|
},
|
|
},
|
|
{
|
|
fName: "repeated_float",
|
|
set: func() { x.SetRepeatedFloat([]float32{}) },
|
|
setNil: func() { x.SetRepeatedFloat(nil) },
|
|
len: func() int {
|
|
if x.GetRepeatedFloat() == nil {
|
|
return -1
|
|
}
|
|
return len(x.GetRepeatedFloat())
|
|
},
|
|
},
|
|
{
|
|
fName: "repeated_double",
|
|
set: func() { x.SetRepeatedDouble([]float64{}) },
|
|
setNil: func() { x.SetRepeatedDouble(nil) },
|
|
len: func() int {
|
|
if x.GetRepeatedDouble() == nil {
|
|
return -1
|
|
}
|
|
return len(x.GetRepeatedDouble())
|
|
},
|
|
},
|
|
{
|
|
fName: "repeated_bool",
|
|
set: func() { x.SetRepeatedBool([]bool{}) },
|
|
setNil: func() { x.SetRepeatedBool(nil) },
|
|
len: func() int {
|
|
if x.GetRepeatedBool() == nil {
|
|
return -1
|
|
}
|
|
return len(x.GetRepeatedBool())
|
|
},
|
|
},
|
|
{
|
|
fName: "repeated_string",
|
|
set: func() { x.SetRepeatedString([]string{}) },
|
|
setNil: func() { x.SetRepeatedString(nil) },
|
|
len: func() int {
|
|
if x.GetRepeatedString() == nil {
|
|
return -1
|
|
}
|
|
return len(x.GetRepeatedString())
|
|
},
|
|
},
|
|
{
|
|
fName: "repeated_bytes",
|
|
set: func() { x.SetRepeatedBytes([][]byte{}) },
|
|
setNil: func() { x.SetRepeatedBytes(nil) },
|
|
len: func() int {
|
|
if x.GetRepeatedBytes() == nil {
|
|
return -1
|
|
}
|
|
return len(x.GetRepeatedBytes())
|
|
},
|
|
},
|
|
{
|
|
fName: "RepeatedGroup",
|
|
set: func() { x.SetRepeatedgroup([]*testhybridpb.TestAllTypes_RepeatedGroup{}) },
|
|
setNil: func() { x.SetRepeatedgroup(nil) },
|
|
len: func() int {
|
|
if x.GetRepeatedgroup() == nil {
|
|
return -1
|
|
}
|
|
return len(x.GetRepeatedgroup())
|
|
},
|
|
},
|
|
{
|
|
fName: "repeated_nested_message",
|
|
set: func() { x.SetRepeatedNestedMessage([]*testhybridpb.TestAllTypes_NestedMessage{}) },
|
|
setNil: func() { x.SetRepeatedNestedMessage(nil) },
|
|
len: func() int {
|
|
if x.GetRepeatedNestedMessage() == nil {
|
|
return -1
|
|
}
|
|
return len(x.GetRepeatedNestedMessage())
|
|
},
|
|
},
|
|
{
|
|
fName: "repeated_nested_enum",
|
|
set: func() { x.SetRepeatedNestedEnum([]testhybridpb.TestAllTypes_NestedEnum{}) },
|
|
setNil: func() { x.SetRepeatedNestedEnum(nil) },
|
|
len: func() int {
|
|
if x.GetRepeatedNestedEnum() == nil {
|
|
return -1
|
|
}
|
|
return len(x.GetRepeatedNestedEnum())
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, mv := range tab {
|
|
if mv.len() != -1 {
|
|
t.Errorf("Repeated field %s was not nil to start with ", mv.fName)
|
|
}
|
|
mv.set()
|
|
if mv.len() != 0 {
|
|
t.Errorf("Repeated field %s did not retain empty slice ", mv.fName)
|
|
}
|
|
b, err := proto.Marshal(x)
|
|
if err != nil {
|
|
t.Fatalf("Failed to marshal message, err = %v", err)
|
|
}
|
|
proto.Unmarshal(b, x)
|
|
if mv.len() != -1 {
|
|
t.Errorf("Repeated field %s was not nil to start with ", mv.fName)
|
|
}
|
|
mv.set()
|
|
mv.setNil()
|
|
if mv.len() != -1 {
|
|
t.Errorf("Repeated field %s was not nil event though we set it to ", mv.fName)
|
|
}
|
|
|
|
}
|
|
|
|
// Check that we actually retain the same slice
|
|
s := make([]testhybridpb.TestAllTypes_NestedEnum, 0, 455)
|
|
x.SetRepeatedNestedEnum(s)
|
|
if got, want := cap(x.GetRepeatedNestedEnum()), 455; got != want {
|
|
t.Errorf("cap(x.GetRepeatedNestedEnum()) returned %v, expected %v", got, want)
|
|
}
|
|
// Do this for a message too
|
|
s2 := make([]*testhybridpb.TestAllTypes_NestedMessage, 0, 544)
|
|
x.SetRepeatedNestedMessage(s2)
|
|
if got, want := cap(x.GetRepeatedNestedMessage()), 544; got != want {
|
|
t.Errorf("cap(x.GetRepeatedNestedMessage()) returned %v, expected %v", got, want)
|
|
}
|
|
// Check special bytes behavior
|
|
x.SetOptionalBytes(nil)
|
|
if got, want := x.HasOptionalBytes(), true; got != want {
|
|
t.Errorf("HasOptionalBytes after setting to nil returned %v, expected %v", got, want)
|
|
}
|
|
if got := x.GetOptionalBytes(); got == nil || len(got) != 0 {
|
|
t.Errorf("GetOptionalBytes after setting to nil returned %v, expected %v", got, []byte{})
|
|
}
|
|
|
|
}
|
|
|
|
func TestOpaqueSetRepeatedNilReceiver(t *testing.T) {
|
|
var x *testopaquepb.TestAllTypes
|
|
expectPanic(t, func() {
|
|
x.SetRepeatedUint32(nil)
|
|
}, "Setting repeated field on nil receiver did not panic.")
|
|
}
|
|
|
|
func TestOpaqueSetRepeated(t *testing.T) {
|
|
for _, mode := range []bool{true, false} {
|
|
impl.EnableLazyUnmarshal(mode)
|
|
t.Run(fmt.Sprintf("LazyUnmarshal_%t", mode), testOpaqueSetRepeatedSub)
|
|
}
|
|
}
|
|
|
|
func testOpaqueSetRepeatedSub(t *testing.T) {
|
|
x := &testopaquepb.TestAllTypes{}
|
|
|
|
tab := []struct {
|
|
fName string // Field name (in proto)
|
|
set func() // Set the field to empty slice
|
|
setNil func() // Set the field to nil
|
|
len func() int // length of field, -1 if nil
|
|
}{
|
|
{
|
|
fName: "repeated_int32",
|
|
set: func() { x.SetRepeatedInt32([]int32{}) },
|
|
setNil: func() { x.SetRepeatedInt32(nil) },
|
|
len: func() int {
|
|
if x.GetRepeatedInt32() == nil {
|
|
return -1
|
|
}
|
|
return len(x.GetRepeatedInt32())
|
|
},
|
|
},
|
|
{
|
|
fName: "repeated_int64",
|
|
set: func() { x.SetRepeatedInt64([]int64{}) },
|
|
setNil: func() { x.SetRepeatedInt64(nil) },
|
|
len: func() int {
|
|
if x.GetRepeatedInt64() == nil {
|
|
return -1
|
|
}
|
|
return len(x.GetRepeatedInt64())
|
|
},
|
|
},
|
|
{
|
|
fName: "repeated_uint32",
|
|
set: func() { x.SetRepeatedUint32([]uint32{}) },
|
|
setNil: func() { x.SetRepeatedUint32(nil) },
|
|
len: func() int {
|
|
if x.GetRepeatedUint32() == nil {
|
|
return -1
|
|
}
|
|
return len(x.GetRepeatedUint32())
|
|
},
|
|
},
|
|
{
|
|
fName: "repeated_uint64",
|
|
set: func() { x.SetRepeatedUint64([]uint64{}) },
|
|
setNil: func() { x.SetRepeatedUint64(nil) },
|
|
len: func() int {
|
|
if x.GetRepeatedUint64() == nil {
|
|
return -1
|
|
}
|
|
return len(x.GetRepeatedUint64())
|
|
},
|
|
},
|
|
{
|
|
fName: "repeated_sint32",
|
|
set: func() { x.SetRepeatedSint32([]int32{}) },
|
|
setNil: func() { x.SetRepeatedSint32(nil) },
|
|
len: func() int {
|
|
if x.GetRepeatedSint32() == nil {
|
|
return -1
|
|
}
|
|
return len(x.GetRepeatedSint32())
|
|
},
|
|
},
|
|
{
|
|
fName: "repeated_sint64",
|
|
set: func() { x.SetRepeatedSint64([]int64{}) },
|
|
setNil: func() { x.SetRepeatedSint64(nil) },
|
|
len: func() int {
|
|
if x.GetRepeatedSint64() == nil {
|
|
return -1
|
|
}
|
|
return len(x.GetRepeatedSint64())
|
|
},
|
|
},
|
|
{
|
|
fName: "repeated_fixed32",
|
|
set: func() { x.SetRepeatedFixed32([]uint32{}) },
|
|
setNil: func() { x.SetRepeatedFixed32(nil) },
|
|
len: func() int {
|
|
if x.GetRepeatedFixed32() == nil {
|
|
return -1
|
|
}
|
|
return len(x.GetRepeatedFixed32())
|
|
},
|
|
},
|
|
{
|
|
fName: "repeated_fixed64",
|
|
set: func() { x.SetRepeatedFixed64([]uint64{}) },
|
|
setNil: func() { x.SetRepeatedFixed64(nil) },
|
|
len: func() int {
|
|
if x.GetRepeatedFixed64() == nil {
|
|
return -1
|
|
}
|
|
return len(x.GetRepeatedFixed64())
|
|
},
|
|
},
|
|
{
|
|
fName: "repeated_sfixed32",
|
|
set: func() { x.SetRepeatedSfixed32([]int32{}) },
|
|
setNil: func() { x.SetRepeatedSfixed32(nil) },
|
|
len: func() int {
|
|
if x.GetRepeatedSfixed32() == nil {
|
|
return -1
|
|
}
|
|
return len(x.GetRepeatedSfixed32())
|
|
},
|
|
},
|
|
{
|
|
fName: "repeated_sfixed64",
|
|
set: func() { x.SetRepeatedSfixed64([]int64{}) },
|
|
setNil: func() { x.SetRepeatedSfixed64(nil) },
|
|
len: func() int {
|
|
if x.GetRepeatedSfixed64() == nil {
|
|
return -1
|
|
}
|
|
return len(x.GetRepeatedSfixed64())
|
|
},
|
|
},
|
|
{
|
|
fName: "repeated_float",
|
|
set: func() { x.SetRepeatedFloat([]float32{}) },
|
|
setNil: func() { x.SetRepeatedFloat(nil) },
|
|
len: func() int {
|
|
if x.GetRepeatedFloat() == nil {
|
|
return -1
|
|
}
|
|
return len(x.GetRepeatedFloat())
|
|
},
|
|
},
|
|
{
|
|
fName: "repeated_double",
|
|
set: func() { x.SetRepeatedDouble([]float64{}) },
|
|
setNil: func() { x.SetRepeatedDouble(nil) },
|
|
len: func() int {
|
|
if x.GetRepeatedDouble() == nil {
|
|
return -1
|
|
}
|
|
return len(x.GetRepeatedDouble())
|
|
},
|
|
},
|
|
{
|
|
fName: "repeated_bool",
|
|
set: func() { x.SetRepeatedBool([]bool{}) },
|
|
setNil: func() { x.SetRepeatedBool(nil) },
|
|
len: func() int {
|
|
if x.GetRepeatedBool() == nil {
|
|
return -1
|
|
}
|
|
return len(x.GetRepeatedBool())
|
|
},
|
|
},
|
|
{
|
|
fName: "repeated_string",
|
|
set: func() { x.SetRepeatedString([]string{}) },
|
|
setNil: func() { x.SetRepeatedString(nil) },
|
|
len: func() int {
|
|
if x.GetRepeatedString() == nil {
|
|
return -1
|
|
}
|
|
return len(x.GetRepeatedString())
|
|
},
|
|
},
|
|
{
|
|
fName: "repeated_bytes",
|
|
set: func() { x.SetRepeatedBytes([][]byte{}) },
|
|
setNil: func() { x.SetRepeatedBytes(nil) },
|
|
len: func() int {
|
|
if x.GetRepeatedBytes() == nil {
|
|
return -1
|
|
}
|
|
return len(x.GetRepeatedBytes())
|
|
},
|
|
},
|
|
{
|
|
fName: "RepeatedGroup",
|
|
set: func() { x.SetRepeatedgroup([]*testopaquepb.TestAllTypes_RepeatedGroup{}) },
|
|
setNil: func() { x.SetRepeatedgroup(nil) },
|
|
len: func() int {
|
|
if x.GetRepeatedgroup() == nil {
|
|
return -1
|
|
}
|
|
return len(x.GetRepeatedgroup())
|
|
},
|
|
},
|
|
{
|
|
fName: "repeated_nested_message",
|
|
set: func() { x.SetRepeatedNestedMessage([]*testopaquepb.TestAllTypes_NestedMessage{}) },
|
|
setNil: func() { x.SetRepeatedNestedMessage(nil) },
|
|
len: func() int {
|
|
if x.GetRepeatedNestedMessage() == nil {
|
|
return -1
|
|
}
|
|
return len(x.GetRepeatedNestedMessage())
|
|
},
|
|
},
|
|
{
|
|
fName: "repeated_nested_enum",
|
|
set: func() { x.SetRepeatedNestedEnum([]testopaquepb.TestAllTypes_NestedEnum{}) },
|
|
setNil: func() { x.SetRepeatedNestedEnum(nil) },
|
|
len: func() int {
|
|
if x.GetRepeatedNestedEnum() == nil {
|
|
return -1
|
|
}
|
|
return len(x.GetRepeatedNestedEnum())
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, mv := range tab {
|
|
if mv.len() != -1 {
|
|
t.Errorf("Repeated field %s was not nil to start with ", mv.fName)
|
|
}
|
|
mv.set()
|
|
if mv.len() != 0 {
|
|
t.Errorf("Repeated field %s did not retain empty slice ", mv.fName)
|
|
}
|
|
b, err := proto.Marshal(x)
|
|
if err != nil {
|
|
t.Fatalf("Failed to marshal message, err = %v", err)
|
|
}
|
|
proto.Unmarshal(b, x)
|
|
if mv.len() != -1 {
|
|
t.Errorf("Repeated field %s was not nil to start with ", mv.fName)
|
|
}
|
|
mv.set()
|
|
mv.setNil()
|
|
if mv.len() != -1 {
|
|
t.Errorf("Repeated field %s was not nil event though we set it to ", mv.fName)
|
|
}
|
|
|
|
}
|
|
|
|
// Check that we actually retain the same slice
|
|
s := make([]testopaquepb.TestAllTypes_NestedEnum, 0, 455)
|
|
x.SetRepeatedNestedEnum(s)
|
|
if got, want := cap(x.GetRepeatedNestedEnum()), 455; got != want {
|
|
t.Errorf("cap(x.GetRepeatedNestedEnum()) returned %v, expected %v", got, want)
|
|
}
|
|
// Do this for a message too
|
|
s2 := make([]*testopaquepb.TestAllTypes_NestedMessage, 0, 544)
|
|
x.SetRepeatedNestedMessage(s2)
|
|
if got, want := cap(x.GetRepeatedNestedMessage()), 544; got != want {
|
|
t.Errorf("cap(x.GetRepeatedNestedMessage()) returned %v, expected %v", got, want)
|
|
t.Errorf("present: %v, isNilen: %v", checkPresent(x, 34), x.GetRepeatedNestedMessage())
|
|
}
|
|
// Check special bytes behavior
|
|
x.SetOptionalBytes(nil)
|
|
if got, want := x.HasOptionalBytes(), true; got != want {
|
|
t.Errorf("HasOptionalBytes after setting to nil returned %v, expected %v", got, want)
|
|
}
|
|
if got := x.GetOptionalBytes(); got == nil || len(got) != 0 {
|
|
t.Errorf("GetOptionalBytes after setting to nil returned %v, expected %v", got, []byte{})
|
|
}
|
|
}
|
|
|
|
func checkPresent(m proto.Message, fn uint32) bool {
|
|
vv := reflect.ValueOf(m).Elem()
|
|
rf := vv.FieldByName("XXX_presence")
|
|
rf = reflect.NewAt(rf.Type(), unsafe.Pointer(rf.UnsafeAddr())).Elem()
|
|
ai := int(fn) / 32
|
|
bit := fn % 32
|
|
ptr := rf.Index(ai).Addr().Interface().(*uint32)
|
|
return (*ptr & (1 << bit)) > 0
|
|
}
|