protobuf-go/proto/repeated_test.go
Michael Stapelberg eb7b468655 all: Release the Opaque API
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>
2024-12-11 03:16:51 -08:00

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
}