reflect/protoreflect: register all desciptors in Files

This change makes it such that Files now functionally registers all
descriptors in a file (not just enums, messages, extensions, and services),
but also including enum values, messages fields/oneofs, and service methods.

The ability to look up any descriptor by full name is needed to:
1) properly detect namespace conflicts on enum values
2) properly implement the relative name lookup logic in reflect/protodesc

The approach taken:
1) Assumes that a FileDescriptor has no internal name conflicts.
This will (in a future CL) be guaranteed by reflect/protodesc and
is guaranteed today by protoc for generated descriptors.
2) Observes that the only declarations that can possibly conflict
with another file are top-level declarations (i.e., enums, enum values,
messages, extensions, and services). Enum values are annoying
since they live in the same scope as the parent enum, rather than
being under the enum.

For the internal data structure of Files, we only register the top-level
declarations. This is the bare minimum needed to detect whether the file
being registered has any namespace conflicts with previously registered files.

We shift the effort to lookups, where we now need to peel off the end fragments
of a full name until we find a match in the internal registry. If a match
is found, we may need to descend into that declaration to find a nested
declaration by name.

For initialization, we modify internal/filedesc to initialize the
enum values for all top-level enums. This performance cost is offsetted
by the fact that Files.Register now avoids internally registering
nested enums, messages, and extensions.

For lookup, the cost has shifted from O(1) to O(N),
where N is the number of segments in the full name.
Top-level descriptors still have O(1) lookup times.
Nested descriptors have O(M) lookup times,
where M is the level of nesting within a single file.

Change-Id: I950163423431f04a503b6201ddcc20a62ccba017
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/183697
Reviewed-by: Damien Neil <dneil@google.com>
This commit is contained in:
Joe Tsai 2019-06-21 14:25:16 -07:00 committed by Joe Tsai
parent a3456c946b
commit 424139789a
6 changed files with 279 additions and 144 deletions

View File

@ -109,7 +109,9 @@ type (
L1 EnumL1
L2 *EnumL2 // protected by fileDesc.once
}
EnumL1 struct{}
EnumL1 struct {
eagerValues bool // controls whether EnumL2.Values is already populated
}
EnumL2 struct {
Options func() pref.ProtoMessage
Values EnumValues
@ -133,11 +135,16 @@ func (ed *Enum) Options() pref.ProtoMessage {
}
return descopts.Enum
}
func (ed *Enum) Values() pref.EnumValueDescriptors { return &ed.lazyInit().Values }
func (ed *Enum) ReservedNames() pref.Names { return &ed.lazyInit().ReservedNames }
func (ed *Enum) ReservedRanges() pref.EnumRanges { return &ed.lazyInit().ReservedRanges }
func (ed *Enum) Format(s fmt.State, r rune) { descfmt.FormatDesc(s, r, ed) }
func (ed *Enum) ProtoType(pref.EnumDescriptor) {}
func (ed *Enum) Values() pref.EnumValueDescriptors {
if ed.L1.eagerValues {
return &ed.L2.Values
}
return &ed.lazyInit().Values
}
func (ed *Enum) ReservedNames() pref.Names { return &ed.lazyInit().ReservedNames }
func (ed *Enum) ReservedRanges() pref.EnumRanges { return &ed.lazyInit().ReservedRanges }
func (ed *Enum) Format(s fmt.State, r rune) { descfmt.FormatDesc(s, r, ed) }
func (ed *Enum) ProtoType(pref.EnumDescriptor) {}
func (ed *Enum) lazyInit() *EnumL2 {
ed.L0.ParentFile.lazyInit() // implicitly initializes L2
return ed.L2

View File

@ -221,7 +221,8 @@ func (ed *Enum) unmarshalSeed(b []byte, nb *nameBuilder, pf *File, pd pref.Descr
ed.L0.Parent = pd
ed.L0.Index = i
for len(b) > 0 {
var numValues int
for b := b; len(b) > 0; {
num, typ, n := wire.ConsumeTag(b)
b = b[n:]
switch typ {
@ -231,6 +232,34 @@ func (ed *Enum) unmarshalSeed(b []byte, nb *nameBuilder, pf *File, pd pref.Descr
switch num {
case fieldnum.EnumDescriptorProto_Name:
ed.L0.FullName = nb.AppendFullName(pd.FullName(), v)
case fieldnum.EnumDescriptorProto_Value:
numValues++
}
default:
m := wire.ConsumeFieldValue(num, typ, b)
b = b[m:]
}
}
// Only construct enum value descriptors for top-level enums since
// they are needed for registration.
if pd != pf {
return
}
ed.L1.eagerValues = true
ed.L2 = new(EnumL2)
ed.L2.Values.List = make([]EnumValue, numValues)
for i := 0; len(b) > 0; {
num, typ, n := wire.ConsumeTag(b)
b = b[n:]
switch typ {
case wire.BytesType:
v, m := wire.ConsumeBytes(b)
b = b[m:]
switch num {
case fieldnum.EnumDescriptorProto_Value:
ed.L2.Values.List[i].unmarshalFull(v, nb, pf, ed, i)
i++
}
default:
m := wire.ConsumeFieldValue(num, typ, b)

View File

@ -187,7 +187,9 @@ func (fd *File) unmarshalFull(b []byte) {
func (ed *Enum) unmarshalFull(b []byte, nb *nameBuilder) {
var rawValues [][]byte
var rawOptions []byte
ed.L2 = new(EnumL2)
if !ed.L1.eagerValues {
ed.L2 = new(EnumL2)
}
for len(b) > 0 {
num, typ, n := wire.ConsumeTag(b)
b = b[n:]
@ -210,7 +212,7 @@ func (ed *Enum) unmarshalFull(b []byte, nb *nameBuilder) {
b = b[m:]
}
}
if len(rawValues) > 0 {
if !ed.L1.eagerValues && len(rawValues) > 0 {
ed.L2.Values.List = make([]EnumValue, len(rawValues))
for i, b := range rawValues {
ed.L2.Values.List[i].unmarshalFull(b, nb, ed.L0.ParentFile, ed, i)

View File

@ -24,6 +24,8 @@ type Resolver interface {
FindFileByPath(string) (protoreflect.FileDescriptor, error)
FindEnumByName(protoreflect.FullName) (protoreflect.EnumDescriptor, error)
FindMessageByName(protoreflect.FullName) (protoreflect.MessageDescriptor, error)
// TODO: use FindDescriptorByName instead.
}
// TODO: Should we be responsible for validating other parts of the descriptor

View File

@ -43,16 +43,19 @@ var NotFound = errors.New("not found")
// descriptors contained within them.
// The Find and Range methods are safe for concurrent use.
type Files struct {
// The map of descs contains:
// The map of descsByName contains:
// EnumDescriptor
// EnumValueDescriptor
// MessageDescriptor
// ExtensionDescriptor
// ServiceDescriptor
// *packageDescriptor
//
// Note that files are stored as a slice, since a package may contain
// multiple files.
descs map[protoreflect.FullName]interface{}
// multiple files. Only top-level declarations are registered.
// Note that enum values are in the top-level since that are in the same
// scope as the parent enum.
descsByName map[protoreflect.FullName]interface{}
filesByPath map[string]protoreflect.FileDescriptor
}
@ -61,22 +64,14 @@ type packageDescriptor struct {
}
// NewFiles returns a registry initialized with the provided set of files.
// If there are duplicates, the first one takes precedence.
// Files with a namespace conflict with an pre-existing file are not registered.
func NewFiles(files ...protoreflect.FileDescriptor) *Files {
// TODO: Should last take precedence? This allows a user to intentionally
// overwrite an existing registration.
//
// The use case is for implementing the existing v1 proto.RegisterFile
// function where the behavior is last wins. However, it could be argued
// that the v1 behavior is broken, and we can switch to first wins
// without violating compatibility.
r := new(Files)
r.Register(files...) // ignore errors; first takes precedence
return r
}
// Register registers the provided list of file descriptors.
// Placeholder files are ignored.
//
// If any descriptor within a file conflicts with the descriptor of any
// previously registered file (e.g., two enums with the same full name),
@ -84,8 +79,8 @@ func NewFiles(files ...protoreflect.FileDescriptor) *Files {
//
// It is permitted for multiple files to have the same file path.
func (r *Files) Register(files ...protoreflect.FileDescriptor) error {
if r.descs == nil {
r.descs = map[protoreflect.FullName]interface{}{
if r.descsByName == nil {
r.descsByName = map[protoreflect.FullName]interface{}{
"": &packageDescriptor{},
}
r.filesByPath = make(map[string]protoreflect.FileDescriptor)
@ -98,51 +93,139 @@ func (r *Files) Register(files ...protoreflect.FileDescriptor) error {
}
return firstErr
}
func (r *Files) registerFile(file protoreflect.FileDescriptor) error {
path := file.Path()
func (r *Files) registerFile(fd protoreflect.FileDescriptor) error {
path := fd.Path()
if r.filesByPath[path] != nil {
return errors.New("file %q is already registered", file.Path())
return errors.New("file %q is already registered", fd.Path())
}
for name := file.Package(); name != ""; name = name.Parent() {
switch r.descs[name].(type) {
for name := fd.Package(); name != ""; name = name.Parent() {
switch r.descsByName[name].(type) {
case nil, *packageDescriptor:
default:
return errors.New("file %q has a name conflict over %v", file.Path(), name)
return errors.New("file %q has a name conflict over %v", fd.Path(), name)
}
}
var err error
rangeRegisteredDescriptors(file, func(desc protoreflect.Descriptor) {
if r.descs[desc.FullName()] != nil {
err = errors.New("file %q has a name conflict over %v", file.Path(), desc.FullName())
rangeTopLevelDescriptors(fd, func(d protoreflect.Descriptor) {
if r.descsByName[d.FullName()] != nil {
err = errors.New("file %q has a name conflict over %v", fd.Path(), d.FullName())
}
})
if err != nil {
return err
}
for name := file.Package(); name != ""; name = name.Parent() {
if r.descs[name] == nil {
r.descs[name] = &packageDescriptor{}
for name := fd.Package(); name != ""; name = name.Parent() {
if r.descsByName[name] == nil {
r.descsByName[name] = &packageDescriptor{}
}
}
p := r.descs[file.Package()].(*packageDescriptor)
p.files = append(p.files, file)
rangeRegisteredDescriptors(file, func(desc protoreflect.Descriptor) {
r.descs[desc.FullName()] = desc
p := r.descsByName[fd.Package()].(*packageDescriptor)
p.files = append(p.files, fd)
rangeTopLevelDescriptors(fd, func(d protoreflect.Descriptor) {
r.descsByName[d.FullName()] = d
})
r.filesByPath[path] = file
r.filesByPath[path] = fd
return nil
}
// FindDescriptorByName looks up a descriptor by the full name.
//
// This returns (nil, NotFound) if not found.
func (r *Files) FindDescriptorByName(name protoreflect.FullName) (protoreflect.Descriptor, error) {
if r == nil {
return nil, NotFound
}
prefix := name
suffix := nameSuffix("")
for prefix != "" {
if d, ok := r.descsByName[prefix]; ok {
switch d := d.(type) {
case protoreflect.EnumDescriptor:
if d.FullName() == name {
return d, nil
}
case protoreflect.EnumValueDescriptor:
if d.FullName() == name {
return d, nil
}
case protoreflect.MessageDescriptor:
if d.FullName() == name {
return d, nil
}
if d := findDescriptorInMessage(d, suffix); d != nil && d.FullName() == name {
return d, nil
}
case protoreflect.ExtensionDescriptor:
if d.FullName() == name {
return d, nil
}
case protoreflect.ServiceDescriptor:
if d.FullName() == name {
return d, nil
}
if d := d.Methods().ByName(suffix.Pop()); d != nil && d.FullName() == name {
return d, nil
}
}
return nil, NotFound
}
prefix = prefix.Parent()
suffix = nameSuffix(name[len(prefix)+len("."):])
}
return nil, NotFound
}
func findDescriptorInMessage(md protoreflect.MessageDescriptor, suffix nameSuffix) protoreflect.Descriptor {
name := suffix.Pop()
if suffix == "" {
if ed := md.Enums().ByName(name); ed != nil {
return ed
}
for i := md.Enums().Len() - 1; i >= 0; i-- {
if vd := md.Enums().Get(i).Values().ByName(name); vd != nil {
return vd
}
}
if xd := md.Extensions().ByName(name); xd != nil {
return xd
}
if fd := md.Fields().ByName(name); fd != nil {
return fd
}
if od := md.Oneofs().ByName(name); od != nil {
return od
}
}
if md := md.Messages().ByName(name); md != nil {
if suffix == "" {
return md
}
return findDescriptorInMessage(md, suffix)
}
return nil
}
type nameSuffix string
func (s *nameSuffix) Pop() (name protoreflect.Name) {
if i := strings.IndexByte(string(*s), '.'); i >= 0 {
name, *s = protoreflect.Name((*s)[:i]), (*s)[i+1:]
} else {
name, *s = protoreflect.Name((*s)), ""
}
return name
}
// FindEnumByName looks up an enum by the enum's full name.
//
// This returns (nil, NotFound) if not found.
//
// Deprecated: Use FindDescriptorByName instead.
func (r *Files) FindEnumByName(name protoreflect.FullName) (protoreflect.EnumDescriptor, error) {
if r == nil {
return nil, NotFound
}
if d, ok := r.descs[name].(protoreflect.EnumDescriptor); ok {
d, _ := r.FindDescriptorByName(name)
if d, ok := d.(protoreflect.EnumDescriptor); ok {
return d, nil
}
return nil, NotFound
@ -151,11 +234,11 @@ func (r *Files) FindEnumByName(name protoreflect.FullName) (protoreflect.EnumDes
// FindMessageByName looks up a message by the message's full name.
//
// This returns (nil, NotFound) if not found.
//
// Deprecated: Use FindDescriptorByName instead.
func (r *Files) FindMessageByName(name protoreflect.FullName) (protoreflect.MessageDescriptor, error) {
if r == nil {
return nil, NotFound
}
if d, ok := r.descs[name].(protoreflect.MessageDescriptor); ok {
d, _ := r.FindDescriptorByName(name)
if d, ok := d.(protoreflect.MessageDescriptor); ok {
return d, nil
}
return nil, NotFound
@ -167,11 +250,11 @@ func (r *Files) FindMessageByName(name protoreflect.FullName) (protoreflect.Mess
// message being extended.
//
// This returns (nil, NotFound) if not found.
//
// Deprecated: Use FindDescriptorByName instead.
func (r *Files) FindExtensionByName(name protoreflect.FullName) (protoreflect.ExtensionDescriptor, error) {
if r == nil {
return nil, NotFound
}
if d, ok := r.descs[name].(protoreflect.ExtensionDescriptor); ok {
d, _ := r.FindDescriptorByName(name)
if d, ok := d.(protoreflect.ExtensionDescriptor); ok {
return d, nil
}
return nil, NotFound
@ -180,11 +263,11 @@ func (r *Files) FindExtensionByName(name protoreflect.FullName) (protoreflect.Ex
// FindServiceByName looks up a service by the service's full name.
//
// This returns (nil, NotFound) if not found.
//
// Deprecated: Use FindDescriptorByName instead.
func (r *Files) FindServiceByName(name protoreflect.FullName) (protoreflect.ServiceDescriptor, error) {
if r == nil {
return nil, NotFound
}
if d, ok := r.descs[name].(protoreflect.ServiceDescriptor); ok {
d, _ := r.FindDescriptorByName(name)
if d, ok := d.(protoreflect.ServiceDescriptor); ok {
return d, nil
}
return nil, NotFound
@ -209,7 +292,7 @@ func (r *Files) RangeFiles(f func(protoreflect.FileDescriptor) bool) {
if r == nil {
return
}
for _, d := range r.descs {
for _, d := range r.descsByName {
if p, ok := d.(*packageDescriptor); ok {
for _, file := range p.files {
if !f(file) {
@ -226,7 +309,7 @@ func (r *Files) RangeFilesByPackage(name protoreflect.FullName, f func(protorefl
if r == nil {
return
}
p, ok := r.descs[name].(*packageDescriptor)
p, ok := r.descsByName[name].(*packageDescriptor)
if !ok {
return
}
@ -237,34 +320,29 @@ func (r *Files) RangeFilesByPackage(name protoreflect.FullName, f func(protorefl
}
}
// rangeRegisteredDescriptors iterates over all descriptors in a file which
// will be entered into the registry: enums, messages, extensions, and services.
func rangeRegisteredDescriptors(fd protoreflect.FileDescriptor, f func(protoreflect.Descriptor)) {
rangeRegisteredMessageDescriptors(fd.Messages(), f)
for i := 0; i < fd.Enums().Len(); i++ {
e := fd.Enums().Get(i)
f(e)
}
for i := 0; i < fd.Extensions().Len(); i++ {
f(fd.Extensions().Get(i))
}
for i := 0; i < fd.Services().Len(); i++ {
f(fd.Services().Get(i))
}
}
func rangeRegisteredMessageDescriptors(messages protoreflect.MessageDescriptors, f func(protoreflect.Descriptor)) {
for i := 0; i < messages.Len(); i++ {
md := messages.Get(i)
f(md)
rangeRegisteredMessageDescriptors(md.Messages(), f)
for i := 0; i < md.Enums().Len(); i++ {
e := md.Enums().Get(i)
f(e)
}
for i := 0; i < md.Extensions().Len(); i++ {
f(md.Extensions().Get(i))
// rangeTopLevelDescriptors iterates over all top-level descriptors in a file
// which will be directly entered into the registry.
func rangeTopLevelDescriptors(fd protoreflect.FileDescriptor, f func(protoreflect.Descriptor)) {
eds := fd.Enums()
for i := eds.Len() - 1; i >= 0; i-- {
f(eds.Get(i))
vds := eds.Get(i).Values()
for i := vds.Len() - 1; i >= 0; i-- {
f(vds.Get(i))
}
}
mds := fd.Messages()
for i := mds.Len() - 1; i >= 0; i-- {
f(mds.Get(i))
}
xds := fd.Extensions()
for i := xds.Len() - 1; i >= 0; i-- {
f(xds.Get(i))
}
sds := fd.Services()
for i := sds.Len() - 1; i >= 0; i-- {
f(sds.Get(i))
}
}
// Type is an interface satisfied by protoreflect.EnumType,

View File

@ -18,7 +18,6 @@ import (
pref "google.golang.org/protobuf/reflect/protoreflect"
preg "google.golang.org/protobuf/reflect/protoregistry"
test2pb "google.golang.org/protobuf/internal/testprotos/test"
testpb "google.golang.org/protobuf/reflect/protoregistry/testprotos"
"google.golang.org/protobuf/types/descriptorpb"
)
@ -45,6 +44,10 @@ func TestFiles(t *testing.T) {
inFile pref.FileDescriptor
wantErr string
}
testFindDesc struct {
inName pref.FullName
wantFound bool
}
testRangePkg struct {
inPkg pref.FullName
wantFiles []file
@ -57,6 +60,7 @@ func TestFiles(t *testing.T) {
tests := []struct {
files []testFile
findDescs []testFindDesc
rangePkgs []testRangePkg
findPaths []testFindPath
}{{
@ -148,10 +152,20 @@ func TestFiles(t *testing.T) {
]
oneof_decl: [{name:"Oneof"}]
extension_range: [{start:1000 end:2000}]
enum_type: [
{name:"Enum" value:[{name:"EnumValue" number:0}]}
]
nested_type: [
{name:"Message" field:[{name:"Field" number:1 label:LABEL_OPTIONAL type:TYPE_STRING}]}
]
extension: [
{name:"Extension" number:1001 label:LABEL_OPTIONAL type:TYPE_STRING extendee:".fizz.buzz.Message"}
]
}]
enum_type: [{
name: "Enum"
Value: [{name:"EnumValue" number:0}]
value: [{name:"EnumValue" number:0}]
}]
extension: [
{name:"Extension" number:1000 label:LABEL_OPTIONAL type:TYPE_STRING extendee:".fizz.buzz.Message"}
@ -205,6 +219,59 @@ func TestFiles(t *testing.T) {
}]
`),
}},
findDescs: []testFindDesc{
{inName: "fizz.buzz.message", wantFound: false},
{inName: "fizz.buzz.Message", wantFound: true},
{inName: "fizz.buzz.Message.X", wantFound: false},
{inName: "fizz.buzz.Field", wantFound: false},
{inName: "fizz.buzz.Oneof", wantFound: false},
{inName: "fizz.buzz.Message.Field", wantFound: true},
{inName: "fizz.buzz.Message.Field.X", wantFound: false},
{inName: "fizz.buzz.Message.Oneof", wantFound: true},
{inName: "fizz.buzz.Message.Oneof.X", wantFound: false},
{inName: "fizz.buzz.Message.Message", wantFound: true},
{inName: "fizz.buzz.Message.Message.X", wantFound: false},
{inName: "fizz.buzz.Message.Enum", wantFound: true},
{inName: "fizz.buzz.Message.Enum.X", wantFound: false},
{inName: "fizz.buzz.Message.EnumValue", wantFound: true},
{inName: "fizz.buzz.Message.EnumValue.X", wantFound: false},
{inName: "fizz.buzz.Message.Extension", wantFound: true},
{inName: "fizz.buzz.Message.Extension.X", wantFound: false},
{inName: "fizz.buzz.enum", wantFound: false},
{inName: "fizz.buzz.Enum", wantFound: true},
{inName: "fizz.buzz.Enum.X", wantFound: false},
{inName: "fizz.buzz.EnumValue", wantFound: true},
{inName: "fizz.buzz.EnumValue.X", wantFound: false},
{inName: "fizz.buzz.Enum.EnumValue", wantFound: false},
{inName: "fizz.buzz.Extension", wantFound: true},
{inName: "fizz.buzz.Extension.X", wantFound: false},
{inName: "fizz.buzz.service", wantFound: false},
{inName: "fizz.buzz.Service", wantFound: true},
{inName: "fizz.buzz.Service.X", wantFound: false},
{inName: "fizz.buzz.Method", wantFound: false},
{inName: "fizz.buzz.Service.Method", wantFound: true},
{inName: "fizz.buzz.Service.Method.X", wantFound: false},
{inName: "fizz.buzz.gazz", wantFound: false},
{inName: "fizz.buzz.gazz.Enum", wantFound: true},
{inName: "fizz.buzz.gazz.EnumValue", wantFound: true},
{inName: "fizz.buzz.gazz.Enum.EnumValue", wantFound: false},
{inName: "fizz.buzz", wantFound: false},
{inName: "fizz.buzz.Enum1", wantFound: true},
{inName: "fizz.buzz.EnumValue1", wantFound: true},
{inName: "fizz.buzz.Enum1.EnumValue1", wantFound: false},
{inName: "fizz.buzz.Enum2", wantFound: true},
{inName: "fizz.buzz.EnumValue2", wantFound: true},
{inName: "fizz.buzz.Enum2.EnumValue2", wantFound: false},
{inName: "fizz.buzz.Enum3", wantFound: false},
{inName: "", wantFound: false},
{inName: "Message", wantFound: true},
{inName: "Message.Message", wantFound: true},
{inName: "Message.Message.Message", wantFound: true},
{inName: "Message.Message.Message.Message", wantFound: false},
},
}}
sortFiles := cmpopts.SortSlices(func(x, y file) bool {
@ -220,6 +287,14 @@ func TestFiles(t *testing.T) {
}
}
for _, tc := range tt.findDescs {
d, _ := files.FindDescriptorByName(tc.inName)
gotFound := d != nil
if gotFound != tc.wantFound {
t.Errorf("FindDescriptorByName(%v) find mismatch: got %v, want %v", tc.inName, gotFound, tc.wantFound)
}
}
for _, tc := range tt.rangePkgs {
var gotFiles []file
files.RangeFilesByPackage(tc.inPkg, func(fd pref.FileDescriptor) bool {
@ -244,64 +319,6 @@ func TestFiles(t *testing.T) {
}
}
func TestFilesLookup(t *testing.T) {
files := []pref.FileDescriptor{
test2pb.File_test_test_proto,
test2pb.File_test_test_import_proto,
}
r := preg.NewFiles(files...)
checkEnums := func(enums pref.EnumDescriptors) {
for i := 0; i < enums.Len(); i++ {
want := enums.Get(i)
if got, err := r.FindEnumByName(want.FullName()); err != nil {
t.Errorf("FindEnumByName(%q): unexpected error: %v", want.FullName(), err)
} else if got != want {
t.Errorf("FindEnumByName(%q): found descriptor %v (%p), %p", want.FullName(), got.FullName(), got, want)
}
}
}
checkExtensions := func(exts pref.ExtensionDescriptors) {
for i := 0; i < exts.Len(); i++ {
want := exts.Get(i)
if got, err := r.FindExtensionByName(want.FullName()); err != nil {
t.Errorf("FindExtensionByName(%q): unexpected error: %v", want.FullName(), err)
} else if got != want {
t.Errorf("FindExtensionByName(%q): found descriptor %v (%p), %p", want.FullName(), got.FullName(), got, want)
}
}
}
var checkMessages func(pref.MessageDescriptors)
checkMessages = func(messages pref.MessageDescriptors) {
for i := 0; i < messages.Len(); i++ {
want := messages.Get(i)
if got, err := r.FindMessageByName(want.FullName()); err != nil {
t.Errorf("FindMessageByName(%q): unexpected error: %v", want.FullName(), err)
} else if got != want {
t.Errorf("FindMessageByName(%q): found descriptor %v (%p), %p", want.FullName(), got.FullName(), got, want)
}
checkEnums(want.Enums())
checkExtensions(want.Extensions())
checkMessages(want.Messages())
}
}
checkServices := func(services pref.ServiceDescriptors) {
for i := 0; i < services.Len(); i++ {
want := services.Get(i)
if got, err := r.FindServiceByName(want.FullName()); err != nil {
t.Errorf("FindServiceByName(%q): unexpected error: %v", want.FullName(), err)
} else if got != want {
t.Errorf("FindServiceByName(%q): found descriptor %v (%p), %p", want.FullName(), got.FullName(), got, want)
}
}
}
for _, fd := range files {
checkEnums(fd.Enums())
checkExtensions(fd.Extensions())
checkMessages(fd.Messages())
checkServices(fd.Services())
}
}
func TestTypes(t *testing.T) {
mt1 := pimpl.Export{}.MessageTypeOf(&testpb.Message1{})
et1 := pimpl.Export{}.EnumTypeOf(testpb.Enum1_ONE)