protobuf-go/internal/fileinit/fileinit_test.go
Damien Neil 82a0306187 internal/{fileinit,impl}: minimal weak field support
Weak fields are an obsolete proto1 feature. They have been superseded
by extensions. However, some vestigial support for weak fields does
remain, mostly as Google-internal patches. (They aren't exciting;
extensions really do everything weak fields do in a cleaner and
more portable fashion.)

At the moment, the only visible impact of marking a field [weak=true]
is to exclude it from "internal/fileinit".FileBuilder.DependencyIndexes.
We want to preserve that behavior just in case we ever do add full weak
field support here.

Extend fileinit to look up message descriptors for weak fields in the
global registry. If the descriptor cannot be found, use a placeholder
instead.

Remove special-case handling of weak fields in the impl package. The
code generator doesn't do anything special for them, so they can be
treated as any other field.

Change-Id: Ifa2ee3d30d63680a0eeb59c66ebc9521f38fd660
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/175997
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-05-08 17:24:54 +00:00

116 lines
4.3 KiB
Go

package fileinit_test
import (
"bytes"
"compress/gzip"
"io/ioutil"
"testing"
proto "github.com/golang/protobuf/proto"
testpb "github.com/golang/protobuf/v2/internal/testprotos/test"
"github.com/golang/protobuf/v2/reflect/protodesc"
"github.com/golang/protobuf/v2/reflect/protoreflect"
descriptorpb "github.com/golang/protobuf/v2/types/descriptor"
)
func TestInit(t *testing.T) {
// Compare the FileDescriptorProto for the same test file from two different sources:
//
// 1. The result of passing the fileinit-produced FileDescriptor through protodesc.
// 2. The protoc-generated wire-encoded message.
//
// This serves as a test of both fileinit and protodesc.
got := protodesc.ToFileDescriptorProto(testpb.File_test_test_proto)
want := &descriptorpb.FileDescriptorProto{}
zb, _ := (&testpb.TestAllTypes{}).Descriptor()
r, _ := gzip.NewReader(bytes.NewBuffer(zb))
b, _ := ioutil.ReadAll(r)
if err := proto.Unmarshal(b, want); err != nil {
t.Fatal(err)
}
if !proto.Equal(got, want) {
t.Errorf("protodesc.ToFileDescriptorProto(testpb.Test_protoFile) is not equal to the protoc-generated FileDescriptorProto for internal/testprotos/test/test.proto")
}
// Verify that the test proto file provides exhaustive coverage of all descriptor fields.
seen := make(map[protoreflect.FullName]bool)
visitFields(want.ProtoReflect(), func(field protoreflect.FieldDescriptor) {
seen[field.FullName()] = true
})
ignore := map[protoreflect.FullName]bool{
// The protoreflect descriptors don't include source info.
"google.protobuf.FileDescriptorProto.source_code_info": true,
"google.protobuf.FileDescriptorProto.syntax": true,
// TODO: Test oneof and extension options. Testing these requires extending the
// options messages (because they contain no user-settable fields), but importing
// decriptor.proto from test.proto currently causes an import cycle. Add test
// cases when that import cycle has been fixed.
"google.protobuf.OneofDescriptorProto.options": true,
}
for _, messageName := range []protoreflect.Name{
"FileDescriptorProto",
"DescriptorProto",
"FieldDescriptorProto",
"OneofDescriptorProto",
"EnumDescriptorProto",
"EnumValueDescriptorProto",
"ServiceDescriptorProto",
"MethodDescriptorProto",
} {
message := descriptorpb.File_google_protobuf_descriptor_proto.Messages().ByName(messageName)
for i, fields := 0, message.Fields(); i < fields.Len(); i++ {
if name := fields.Get(i).FullName(); !seen[name] && !ignore[name] {
t.Errorf("No test for descriptor field: %v", name)
}
}
}
// Verify that message descriptors for map entries have no Go type info.
mapEntryName := protoreflect.FullName("goproto.proto.test.TestAllTypes.MapInt32Int32Entry")
d := testpb.File_test_test_proto.Messages().ByName("TestAllTypes").Fields().ByName("map_int32_int32").Message()
if gotName, wantName := d.FullName(), mapEntryName; gotName != wantName {
t.Fatalf("looked up wrong descriptor: got %v, want %v", gotName, wantName)
}
if _, ok := d.(protoreflect.MessageType); ok {
t.Errorf("message descriptor for %v must not implement protoreflect.MessageType", mapEntryName)
}
}
// visitFields calls f for every field set in m and its children.
func visitFields(m protoreflect.Message, f func(protoreflect.FieldDescriptor)) {
typ := m.Type()
k := m.KnownFields()
k.Range(func(num protoreflect.FieldNumber, value protoreflect.Value) bool {
field := typ.Fields().ByNumber(num)
f(field)
switch field.Kind() {
case protoreflect.MessageKind, protoreflect.GroupKind:
if field.Cardinality() == protoreflect.Repeated {
for i, list := 0, value.List(); i < list.Len(); i++ {
visitFields(list.Get(i).Message(), f)
}
} else {
visitFields(value.Message(), f)
}
}
return true
})
}
func TestWeakInit(t *testing.T) {
file := testpb.File_test_test_proto
fd := file.Messages().ByName("TestWeak").Fields().ByName("weak_message")
if want, got := fd.IsWeak(), true; got != want {
t.Errorf("field %v: IsWeak() = %v, want %v", fd.FullName(), want, got)
}
if want, got := fd.Message().IsPlaceholder(), false; got != want {
t.Errorf("field %v: Message.IsPlaceholder() = %v, want %v", fd.FullName(), want, got)
}
if fd.Message().Fields().Len() == 0 {
t.Errorf("field %v: Message().Fields().Len() == 0, want >0", fd.FullName())
}
}