blob: e9d6e8816a88f39f4599c307bd765d0ae6be32df [file] [log] [blame]
// Copyright 2020 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 impl_test
import (
"reflect"
"sync"
"testing"
"unsafe"
"github.com/google/go-cmp/cmp"
"google.golang.org/protobuf/internal/errors"
"google.golang.org/protobuf/internal/flags"
"google.golang.org/protobuf/internal/impl"
"google.golang.org/protobuf/internal/protobuild"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/testing/protocmp"
lazytestpb "google.golang.org/protobuf/internal/testprotos/lazy"
"google.golang.org/protobuf/internal/testprotos/messageset/messagesetpb"
testpb "google.golang.org/protobuf/internal/testprotos/test"
)
func TestLazyExtensions(t *testing.T) {
checkLazy := func(when string, m *testpb.TestAllExtensions, want bool) {
xd := testpb.E_OptionalNestedMessage.TypeDescriptor()
if got := impl.IsLazy(m.ProtoReflect(), xd); got != want {
t.Errorf("%v: m.optional_nested_message lazy=%v, want %v", when, got, want)
}
e := proto.GetExtension(m, testpb.E_OptionalNestedMessage).(*testpb.TestAllExtensions_NestedMessage).Corecursive
if got := impl.IsLazy(e.ProtoReflect(), xd); got != want {
t.Errorf("%v: m.optional_nested_message.corecursive.optional_nested_message lazy=%v, want %v", when, got, want)
}
}
m1 := &testpb.TestAllExtensions{}
protobuild.Message{
"optional_nested_message": protobuild.Message{
"a": 1,
"corecursive": protobuild.Message{
"optional_nested_message": protobuild.Message{
"a": 2,
},
},
},
}.Build(m1.ProtoReflect())
checkLazy("before unmarshal", m1, false)
w, err := proto.Marshal(m1)
if err != nil {
t.Fatal(err)
}
m := &testpb.TestAllExtensions{}
if err := proto.Unmarshal(w, m); err != nil {
t.Fatal(err)
}
checkLazy("after unmarshal", m, flags.LazyUnmarshalExtensions)
}
func TestMessageSetLazy(t *testing.T) {
if !flags.LazyUnmarshalExtensions {
t.Skip("lazy extension unmarshaling disabled; not built with the protolegacy tag")
}
h := &lazytestpb.Holder{Data: &messagesetpb.MessageSet{}}
ext := &lazytestpb.Rabbit{Name: proto.String("Judy")}
proto.SetExtension(h.GetData(), lazytestpb.E_Rabbit_MessageSetExtension, ext)
nh := roundtrip(t, h).(*lazytestpb.Holder)
if extensionIsInitialized(t, nh.GetData(), lazytestpb.E_Rabbit_MessageSetExtension.Field) {
t.Errorf("Extension unexpectedly initialized after Unmarshal")
}
proto.Size(nh)
if extensionIsInitialized(t, nh.GetData(), lazytestpb.E_Rabbit_MessageSetExtension.Field) {
t.Errorf("Extension unexpectedly initialized after Size")
}
proto.Marshal(nh)
if extensionIsInitialized(t, nh.GetData(), lazytestpb.E_Rabbit_MessageSetExtension.Field) {
t.Errorf("Extension unexpectedly initialized after Marshal")
}
if !proto.HasExtension(nh.GetData(), lazytestpb.E_Rabbit_MessageSetExtension) {
t.Fatalf("Can't get extension")
}
if extensionIsInitialized(t, nh.GetData(), lazytestpb.E_Rabbit_MessageSetExtension.Field) {
t.Errorf("Extension unexpectedly initialized after Has")
}
nh = roundtrip(t, nh).(*lazytestpb.Holder)
if extensionIsInitialized(t, nh.GetData(), lazytestpb.E_Rabbit_MessageSetExtension.Field) {
t.Errorf("Extension unexpectedly initialized after Has")
}
if diff := cmp.Diff(h, nh, protocmp.Transform()); diff != "" {
t.Errorf("Got %+v, want %+v, diff:\n%s", nh, h, diff)
}
if got, want := extensionIsInitialized(t, nh.GetData(), lazytestpb.E_Rabbit_MessageSetExtension.Field), true; got != want {
t.Errorf("Extension unexpectedly initialized after Diff")
}
int := proto.GetExtension(nh.GetData(), lazytestpb.E_Rabbit_MessageSetExtension).(*lazytestpb.Rabbit)
if int.GetName() != "Judy" {
t.Errorf("Extension value \"Judy\" not retained, got: %v", int.GetName())
}
if got, want := extensionIsInitialized(t, nh.GetData(), lazytestpb.E_Rabbit_MessageSetExtension.Field), true; got != want {
t.Errorf("Extension unexpectedly initialized after Get")
}
}
var (
// Trees are fully lazy
treeTemplate = &lazytestpb.Tree{
Eucalyptus: proto.Bool(true),
}
spGH = lazytestpb.FlyingFoxSpecies_GREY_HEADED
spP = lazytestpb.FlyingFoxSpecies_SPECTACLED
spLE = lazytestpb.FlyingFoxSpecies_LARGE_EARED
spBB = lazytestpb.FlyingFoxSpecies_BARE_BACKED
spF = lazytestpb.PipistrelleSpecies_FOREST
spR = lazytestpb.PipistrelleSpecies_RUSTY
)
func TestExtensionLazy(t *testing.T) {
if !flags.LazyUnmarshalExtensions {
t.Skip("lazy extension unmarshaling disabled; not built with the protolegacy tag")
}
tree := proto.Clone(treeTemplate)
proto.SetExtension(tree, lazytestpb.E_Bat, &lazytestpb.FlyingFox{Species: &spGH})
proto.SetExtension(tree, lazytestpb.E_BatPup, &lazytestpb.FlyingFox{Species: &spP})
nt := roundtrip(t, tree).(*lazytestpb.Tree)
if extensionIsInitialized(t, nt, lazytestpb.E_Bat.Field) {
t.Errorf("Extension unexpectedly initialized after Unmarshal")
}
proto.Size(nt)
if extensionIsInitialized(t, nt, lazytestpb.E_Bat.Field) {
t.Errorf("Extension unexpectedly initialized after Size")
}
gb, err := proto.Marshal(nt)
if err != nil {
t.Fatalf("proto.Marshal(%+v) failed: %v", nt, err)
}
if extensionIsInitialized(t, nt, lazytestpb.E_Bat.Field) {
t.Errorf("Extension unexpectedly initialized after Marshal")
}
fox := proto.GetExtension(nt, lazytestpb.E_Bat).(*lazytestpb.FlyingFox)
if got, want := fox.GetSpecies(), spGH; want != got {
t.Errorf("Extension's Speices field not retained, want: %v, got: %v", want, got)
}
if got, want := extensionIsInitialized(t, nt, lazytestpb.E_Bat.Field), true; got != want {
t.Errorf("Extension unexpectedly initialized after Get")
}
if extensionIsInitialized(t, nt, lazytestpb.E_BatPup.Field) {
t.Errorf("Extension unexpectedly initialized after Get")
}
foxPup := proto.GetExtension(nt, lazytestpb.E_BatPup).(*lazytestpb.FlyingFox)
if got, want := foxPup.GetSpecies(), spP; want != got {
t.Errorf("Extension's Speices field not retained, want: %v, got: %v", want, got)
}
if got, want := extensionIsInitialized(t, nt, lazytestpb.E_Bat.Field), true; got != want {
t.Errorf("Extension unexpectedly initialized after Get")
}
if got, want := extensionIsInitialized(t, nt, lazytestpb.E_BatPup.Field), true; got != want {
t.Errorf("Extension unexpectedly initialized after Get")
}
rt := &lazytestpb.Tree{}
if err := proto.Unmarshal(gb, rt); err != nil {
t.Fatalf("Can't unmarshal pb.Tree: %v", err)
}
if diff := cmp.Diff(tree, rt, protocmp.Transform()); diff != "" {
t.Errorf("Got %+v, want %+v, diff:\n%s", rt, tree, diff)
}
if got, want := extensionIsInitialized(t, rt, lazytestpb.E_Bat.Field), true; got != want {
t.Errorf("Extension unexpectedly initialized after Diff")
}
nt = roundtrip(t, tree).(*lazytestpb.Tree)
proto.ClearExtension(nt, lazytestpb.E_Bat)
proto.ClearExtension(nt, lazytestpb.E_BatPup)
if proto.HasExtension(nt, lazytestpb.E_Bat) {
t.Fatalf("Extension not cleared in (%+v)", nt)
}
if proto.HasExtension(nt, lazytestpb.E_BatPup) {
t.Fatalf("Extension not cleared in (%+v)", nt)
}
}
func TestExtensionNestedScopeLazy(t *testing.T) {
if !flags.LazyUnmarshalExtensions {
t.Skip("lazy extension unmarshaling disabled; not built with the protolegacy tag")
}
tree := proto.Clone(treeTemplate)
proto.SetExtension(tree, lazytestpb.E_BatNest_Bat, &lazytestpb.FlyingFox{Species: &spGH})
nt := roundtrip(t, tree).(*lazytestpb.Tree)
if extensionIsInitialized(t, nt, lazytestpb.E_BatNest_Bat.Field) {
t.Errorf("Extension unexpectedly initialized after Unmarshal")
}
proto.Size(nt)
if extensionIsInitialized(t, nt, lazytestpb.E_BatNest_Bat.Field) {
t.Errorf("Extension unexpectedly initialized after Size")
}
gb, err := proto.Marshal(nt)
if err != nil {
t.Fatalf("proto.Marshal(%+v) failed: %v", nt, err)
}
if extensionIsInitialized(t, nt, lazytestpb.E_BatNest_Bat.Field) {
t.Errorf("Extension unexpectedly initialized after Marshal")
}
fox := proto.GetExtension(nt, lazytestpb.E_BatNest_Bat).(*lazytestpb.FlyingFox)
if got, want := fox.GetSpecies(), spGH; want != got {
t.Errorf("Extension's Speices field not retained, want: %v, got: %v", want, got)
}
if got, want := extensionIsInitialized(t, nt, lazytestpb.E_BatNest_Bat.Field), true; got != want {
t.Errorf("Extension unexpectedly initialized after Get")
}
rt := &lazytestpb.Tree{}
if err := proto.Unmarshal(gb, rt); err != nil {
t.Fatalf("Can't unmarshal pb.Tree: %v", err)
}
if diff := cmp.Diff(tree, rt, protocmp.Transform()); diff != "" {
t.Errorf("Got %+v, want %+v, diff:\n%s", rt, tree, diff)
}
if got, want := extensionIsInitialized(t, rt, lazytestpb.E_BatNest_Bat.Field), true; got != want {
t.Errorf("Extension unexpectedly initialized after Diff")
}
}
func TestExtensionRepeatedMessageLazy(t *testing.T) {
if !flags.LazyUnmarshalExtensions {
t.Skip("lazy extension unmarshaling disabled; not built with the protolegacy tag")
}
posse := []*lazytestpb.FlyingFox{
{Species: &spLE},
{Species: &spBB},
}
m := proto.Clone(treeTemplate)
proto.SetExtension(m, lazytestpb.E_BatPosse, posse)
if got, want := proto.HasExtension(m, lazytestpb.E_BatPosse), true; got != want {
t.Errorf("Extension present after setting: got %v, want %v", got, want)
}
mr := roundtrip(t, m).(*lazytestpb.Tree)
if extensionIsInitialized(t, mr, lazytestpb.E_BatPosse.Field) {
t.Errorf("Extension unexpectedly initialized after Unmarshal")
}
mrr := roundtrip(t, mr).(*lazytestpb.Tree)
if got, want := proto.HasExtension(mrr, lazytestpb.E_BatPosse), true; got != want {
t.Errorf("Extension is not present after setting: got %v, want %v", got, want)
}
if extensionIsInitialized(t, mrr, lazytestpb.E_BatPosse.Field) {
t.Errorf("Extension unexpectedly initialized after Has")
}
mrr = roundtrip(t, mr).(*lazytestpb.Tree)
foxPosse := proto.GetExtension(mrr, lazytestpb.E_BatPosse).([]*lazytestpb.FlyingFox)
if got, want := foxPosse[0].GetSpecies(), spLE; got != want {
t.Errorf("Extension's Speices field, want: %v, got: %v", want, got)
}
if got, want := extensionIsInitialized(t, mrr, lazytestpb.E_BatPosse.Field), true; got != want {
t.Errorf("Extension unexpectedly initialized after Get")
}
// Set to empty slice instead
m = proto.Clone(treeTemplate)
proto.SetExtension(m, lazytestpb.E_BatPosse, []*lazytestpb.FlyingFox{})
if got, want := proto.HasExtension(m, lazytestpb.E_BatPosse), false; got != want {
t.Errorf("Extension present after setting: got %v, want %v", got, want)
}
mr = roundtrip(t, m).(*lazytestpb.Tree)
if got, want := extensionIsInitialized(t, mr, lazytestpb.E_BatPosse.Field), true; got != want {
t.Errorf("Extension unexpectedly initialized after Unmarshal")
}
if got, want := proto.HasExtension(mr, lazytestpb.E_BatPosse), false; got != want {
t.Errorf("Extension is not present after setting: got %v, want %v", got, want)
}
if got, want := extensionIsInitialized(t, mr, lazytestpb.E_BatPosse.Field), true; got != want {
t.Errorf("Extension unexpectedly initialized after Has")
}
mrr = roundtrip(t, mr).(*lazytestpb.Tree)
if got, want := proto.HasExtension(mrr, lazytestpb.E_BatPosse), false; got != want {
t.Errorf("Extension is not present after setting: got %v, want %v", got, want)
}
if got, want := extensionIsInitialized(t, mrr, lazytestpb.E_BatPosse.Field), true; got != want {
t.Errorf("Extension unexpectedly initialized after Has")
}
foxPosse = proto.GetExtension(mrr, lazytestpb.E_BatPosse).([]*lazytestpb.FlyingFox)
if got, want := len(foxPosse), 0; got != want {
t.Errorf("Extension field length, want: %v, got: %v", want, got)
}
if got, want := extensionIsInitialized(t, mrr, lazytestpb.E_BatPosse.Field), true; got != want {
t.Errorf("Extension unexpectedly initialized after Get")
}
}
func TestExtensionIntegerLazy(t *testing.T) {
if !flags.LazyUnmarshalExtensions {
t.Skip("lazy extension unmarshaling disabled; not built with the protolegacy tag")
}
var iBat uint32 = 4711
m := proto.Clone(treeTemplate)
proto.SetExtension(m, lazytestpb.E_IntegerBat, iBat)
if got, want := proto.HasExtension(m, lazytestpb.E_IntegerBat), true; got != want {
t.Errorf("Extension present after setting: got %v, want %v", got, want)
}
mr := roundtrip(t, m).(*lazytestpb.Tree)
if got, want := extensionIsInitialized(t, mr, lazytestpb.E_IntegerBat.Field), true; got != want {
t.Errorf("Extension unexpectedly initialized after Unmarshal")
}
if got, want := proto.HasExtension(mr, lazytestpb.E_IntegerBat), true; got != want {
t.Errorf("Extension is not present after setting: got %v, want %v", got, want)
}
if got, want := extensionIsInitialized(t, mr, lazytestpb.E_IntegerBat.Field), true; got != want {
t.Errorf("Extension unexpectedly initialized after Has")
}
mr = roundtrip(t, m).(*lazytestpb.Tree)
if got, want := proto.GetExtension(mr, lazytestpb.E_IntegerBat).(uint32), iBat; got != want {
t.Errorf("Extension's integer field, want: %v, got: %v", want, got)
}
if got, want := extensionIsInitialized(t, mr, lazytestpb.E_IntegerBat.Field), true; got != want {
t.Errorf("Extension unexpectedly initialized after Get")
}
}
func TestExtensionBinaryLazy(t *testing.T) {
if !flags.LazyUnmarshalExtensions {
t.Skip("lazy extension unmarshaling disabled; not built with the protolegacy tag")
}
m := proto.Clone(treeTemplate)
bBat := []byte("I'm a bat")
proto.SetExtension(m, lazytestpb.E_BinaryBat, bBat)
if got, want := proto.HasExtension(m, lazytestpb.E_BinaryBat), true; got != want {
t.Errorf("Extension present after setting: got %v, want %v", got, want)
}
mr := roundtrip(t, m).(*lazytestpb.Tree)
// A binary extension is never kept lazy
if got, want := extensionIsInitialized(t, mr, lazytestpb.E_BinaryBat.Field), true; got != want {
t.Errorf("Extension unexpectedly initialized after Unmarshal")
}
if got, want := proto.HasExtension(mr, lazytestpb.E_BinaryBat), true; got != want {
t.Errorf("Extension present after roundtrip: got %v, want %v", got, want)
}
m = proto.Clone(treeTemplate)
proto.SetExtension(m, lazytestpb.E_BinaryBat, []byte{})
// An empty binary is also a binary
if got, want := proto.HasExtension(m, lazytestpb.E_BinaryBat), true; got != want {
t.Errorf("Extension present after setting: got %v, want %v", got, want)
}
mr = roundtrip(t, m).(*lazytestpb.Tree)
if got, want := extensionIsInitialized(t, mr, lazytestpb.E_BinaryBat.Field), true; got != want {
t.Errorf("Extension unexpectedly initialized after Unmarshal")
}
if got, want := proto.HasExtension(mr, lazytestpb.E_BinaryBat), true; got != want {
t.Errorf("Extension present after setting: got %v, want %v", got, want)
}
}
func TestExtensionGroupLazy(t *testing.T) {
if !flags.LazyUnmarshalExtensions {
t.Skip("lazy extension unmarshaling disabled; not built with the protolegacy tag")
}
// Group: should behave like message
pip := &lazytestpb.Pipistrelle{Species: &spF}
pips := []*lazytestpb.Pipistrelles{
{Species: &spF},
{Species: &spR},
}
m := proto.Clone(treeTemplate)
proto.SetExtension(m, lazytestpb.E_Pipistrelle, pip)
if got, want := proto.HasExtension(m, lazytestpb.E_Pipistrelle), true; got != want {
t.Errorf("Extension present after setting: got %v, want %v", got, want)
}
mr := roundtrip(t, m).(*lazytestpb.Tree)
if extensionIsInitialized(t, mr, lazytestpb.E_Pipistrelle.Field) {
t.Errorf("Extension unexpectedly initialized after Unmarshal")
}
if got, want := proto.HasExtension(mr, lazytestpb.E_Pipistrelle), true; got != want {
t.Errorf("Extension present after setting: got %v, want %v", got, want)
}
if extensionIsInitialized(t, mr, lazytestpb.E_Pipistrelle.Field) {
t.Errorf("Extension unexpectedly initialized after Has")
}
mrr := roundtrip(t, mr).(*lazytestpb.Tree)
if extensionIsInitialized(t, mrr, lazytestpb.E_Pipistrelle.Field) {
t.Errorf("Extension unexpectedly initialized after Unmarshal")
}
mrr = roundtrip(t, mr).(*lazytestpb.Tree)
if extensionIsInitialized(t, mrr, lazytestpb.E_Pipistrelle.Field) {
t.Errorf("Extension unexpectedly initialized after Unmarshal")
}
pipistrelle := proto.GetExtension(mrr, lazytestpb.E_Pipistrelle).(*lazytestpb.Pipistrelle)
if got, want := pipistrelle.GetSpecies(), spF; got != want {
t.Errorf("Extension's Speices field, want: %v, got: %v", want, got)
}
if got, want := extensionIsInitialized(t, mrr, lazytestpb.E_Pipistrelle.Field), true; got != want {
t.Errorf("Extension unexpectedly initialized after Get")
}
// Group slice, behaves like message slice
m = proto.Clone(treeTemplate)
proto.SetExtension(m, lazytestpb.E_Pipistrelles, pips)
if got, want := proto.HasExtension(m, lazytestpb.E_Pipistrelles), true; got != want {
t.Errorf("Extension present after setting: got %v, want %v", got, want)
}
mr = roundtrip(t, m).(*lazytestpb.Tree)
if extensionIsInitialized(t, mr, lazytestpb.E_Pipistrelles.Field) {
t.Errorf("Extension unexpectedly initialized after Unmarshal")
}
if got, want := proto.HasExtension(mr, lazytestpb.E_Pipistrelles), true; got != want {
t.Errorf("Extension present after setting: got %v, want %v", got, want)
}
if extensionIsInitialized(t, mr, lazytestpb.E_Pipistrelles.Field) {
t.Errorf("Extension unexpectedly initialized after Has")
}
mr = roundtrip(t, m).(*lazytestpb.Tree)
mrr = roundtrip(t, mr).(*lazytestpb.Tree)
if got, want := proto.HasExtension(mrr, lazytestpb.E_Pipistrelles), true; got != want {
t.Errorf("Extension present after setting: got %v, want %v", got, want)
}
if extensionIsInitialized(t, mrr, lazytestpb.E_Pipistrelles.Field) {
t.Errorf("Extension unexpectedly initialized after Has")
}
mrr = roundtrip(t, mr).(*lazytestpb.Tree)
pipistrelles := proto.GetExtension(mrr, lazytestpb.E_Pipistrelles).([]*lazytestpb.Pipistrelles)
if got, want := pipistrelles[1].GetSpecies(), spR; got != want {
t.Errorf("Extension's Speices field, want: %v, got: %v", want, got)
}
if got, want := extensionIsInitialized(t, mrr, lazytestpb.E_Pipistrelles.Field), true; got != want {
t.Errorf("Extension unexpectedly initialized after Get")
}
// Setting an empty group slice has no effect
m = proto.Clone(treeTemplate)
proto.SetExtension(m, lazytestpb.E_Pipistrelles, []*lazytestpb.Pipistrelles{})
if got, want := proto.HasExtension(m, lazytestpb.E_Pipistrelles), false; got != want {
t.Errorf("Extension present after setting empty: got %v, want %v", got, want)
}
mr = roundtrip(t, m).(*lazytestpb.Tree)
if got, want := extensionIsInitialized(t, mr, lazytestpb.E_Pipistrelles.Field), true; got != want {
t.Errorf("Extension unexpectedly initialized after Unmarshal")
}
if got, want := proto.HasExtension(mr, lazytestpb.E_Pipistrelles), false; got != want {
t.Errorf("Extension present after setting: got %v, want %v", got, want)
}
if got, want := extensionIsInitialized(t, mr, lazytestpb.E_Pipistrelles.Field), true; got != want {
t.Errorf("Extension unexpectedly initialized after Has")
}
mrr = roundtrip(t, mr).(*lazytestpb.Tree)
if got, want := proto.HasExtension(mrr, lazytestpb.E_Pipistrelles), false; got != want {
t.Errorf("Extension present after setting: got %v, want %v", got, want)
}
if got, want := extensionIsInitialized(t, mrr, lazytestpb.E_Pipistrelles.Field), true; got != want {
t.Errorf("Extension unexpectedly initialized after Has")
}
noPipistrelles := proto.GetExtension(mrr, lazytestpb.E_Pipistrelles).([]*lazytestpb.Pipistrelles)
if got, want := len(noPipistrelles), 0; got != want {
t.Errorf("Extension's field length, want: %v, got: %v", want, got)
}
if got, want := extensionIsInitialized(t, mrr, lazytestpb.E_Pipistrelles.Field), true; got != want {
t.Errorf("Extension unexpectedly initialized after Get")
}
}
func TestMarshalMessageSetLazyRace(t *testing.T) {
if !flags.LazyUnmarshalExtensions {
t.Skip("lazy extension unmarshaling disabled; not built with the protolegacy tag")
}
h := &lazytestpb.Holder{Data: &messagesetpb.MessageSet{}}
ext := &lazytestpb.Rabbit{Name: proto.String("Judy")}
proto.SetExtension(h.GetData(), lazytestpb.E_Rabbit_MessageSetExtension, ext)
b, err := proto.Marshal(h)
if err != nil {
t.Fatalf("Could not marshal message: %v", err)
}
if err := proto.Unmarshal(b, h); err != nil {
t.Fatalf("Could not unmarshal message: %v", err)
}
// after Unmarshal, the extension is in undecoded form.
// GetExtension will decode it lazily. Make sure this does
// not race against Marshal.
// The following pattern is similar to x/sync/errgroup,
// but we want to avoid adding that dependencies just for a test.
var (
wg sync.WaitGroup
errOnce sync.Once
groupErr error
)
for n := 30; n > 0; n-- {
wg.Add(2)
go func() {
defer wg.Done()
if err := func() error {
b, err := proto.Marshal(h)
if err == nil {
return proto.Unmarshal(b, &lazytestpb.Rabbit{})
}
return err
}(); err != nil {
errOnce.Do(func() { groupErr = err })
}
}()
go func() {
defer wg.Done()
if err := func() error {
if !proto.HasExtension(h.GetData(), lazytestpb.E_Rabbit_MessageSetExtension) {
return errors.New("proto: missing extension")
}
return nil
}(); err != nil {
errOnce.Do(func() { groupErr = err })
}
}()
}
wg.Wait()
if groupErr != nil {
t.Fatal(groupErr)
}
}
// Utility functions for the test cases
// Some functions from pointer_unsafe.go
type pointer struct{ p unsafe.Pointer }
func (p pointer) Apply(f uintptr) pointer {
return pointer{p: unsafe.Pointer(uintptr(p.p) + uintptr(f))}
}
func pointerOfIface(v any) pointer {
type ifaceHeader struct {
Type unsafe.Pointer
Data unsafe.Pointer
}
return pointer{p: (*ifaceHeader)(unsafe.Pointer(&v)).Data}
}
func (p pointer) AsValueOf(t reflect.Type) reflect.Value {
return reflect.NewAt(t, p.p)
}
// Highly implementation dependent - uses unsafe pointers to figure
// out if the lazyExtensionValue is initialized.
func extensionIsInitialized(t *testing.T, data any, fieldNo int32) bool {
ext, ok := reflect.TypeOf(data).Elem().FieldByName("extensionFields")
if !ok {
t.Fatalf("Failed to retrieve offset of field \"extensionFields\".")
}
lazy, ok := reflect.TypeOf((*impl.ExtensionField)(nil)).Elem().FieldByName("lazy")
if !ok {
t.Fatalf("Failed to retrieve offset of field \"lazy\".")
}
pi := pointerOfIface(data)
m, ok := pi.Apply(ext.Offset).AsValueOf(reflect.TypeOf((map[int32]impl.ExtensionField)(nil))).Interface().(*map[int32]impl.ExtensionField)
if !ok {
t.Fatalf("Extension map has unexpected type.")
}
f := (*m)[fieldNo]
// Here we rely on atomicOnce being the first field in the 'lazy' struct.
app, ok := pointerOfIface(&f).Apply(lazy.Offset).AsValueOf(reflect.TypeOf((*uint32)(nil))).Interface().(**uint32)
if !ok {
t.Fatalf("Field atomicOnce does not seem to be the first field, or has changed type.")
}
if *app == nil {
return true // lazy ptr is nil
}
return **app > 0
}
func roundtrip(t *testing.T, m proto.Message) proto.Message {
t.Helper()
n := m.ProtoReflect().Type().New().Interface()
b, err := proto.Marshal(m)
if err != nil {
t.Fatalf("proto.Marshal(%+v) failed: %v", m, err)
}
if err := proto.Unmarshal(b, n); err != nil {
t.Fatalf("proto.Unmarshal failed: %v", err)
}
return n
}