2019-06-06 20:01:53 +00:00
// Copyright 2018 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 filedesc_test
2019-01-26 22:24:59 +00:00
import (
"bytes"
"compress/gzip"
2024-04-22 22:15:31 +00:00
"io"
2019-01-26 22:24:59 +00:00
"testing"
2019-05-16 22:53:25 +00:00
"google.golang.org/protobuf/proto"
2019-05-14 06:55:40 +00:00
"google.golang.org/protobuf/reflect/protodesc"
"google.golang.org/protobuf/reflect/protoreflect"
2019-05-16 19:47:20 +00:00
testpb "google.golang.org/protobuf/internal/testprotos/test"
2019-04-08 20:52:14 +00:00
_ "google.golang.org/protobuf/internal/testprotos/test/weak1"
2019-05-16 19:47:20 +00:00
"google.golang.org/protobuf/types/descriptorpb"
2019-01-26 22:24:59 +00:00
)
2020-02-04 21:10:26 +00:00
var testFile = new ( testpb . TestAllTypes ) . ProtoReflect ( ) . Descriptor ( ) . ParentFile ( )
2019-01-26 22:24:59 +00:00
func TestInit ( t * testing . T ) {
// Compare the FileDescriptorProto for the same test file from two different sources:
//
2019-06-06 20:01:53 +00:00
// 1. The result of passing the filedesc-produced FileDescriptor through protodesc.
2019-01-26 22:24:59 +00:00
// 2. The protoc-generated wire-encoded message.
//
2019-06-06 20:01:53 +00:00
// This serves as a test of both filedesc and protodesc.
2020-02-04 21:10:26 +00:00
got := protodesc . ToFileDescriptorProto ( testFile )
2019-01-26 22:24:59 +00:00
want := & descriptorpb . FileDescriptorProto { }
zb , _ := ( & testpb . TestAllTypes { } ) . Descriptor ( )
r , _ := gzip . NewReader ( bytes . NewBuffer ( zb ) )
2024-04-22 22:15:31 +00:00
b , _ := io . ReadAll ( r )
2019-01-26 22:24:59 +00:00
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
} )
2020-01-11 08:25:01 +00:00
descFile := new ( descriptorpb . DescriptorProto ) . ProtoReflect ( ) . Descriptor ( ) . ParentFile ( )
descPkg := descFile . Package ( )
2019-01-26 22:24:59 +00:00
ignore := map [ protoreflect . FullName ] bool {
// The protoreflect descriptors don't include source info.
2020-01-11 08:25:01 +00:00
descPkg . Append ( "FileDescriptorProto.source_code_info" ) : true ,
descPkg . Append ( "FileDescriptorProto.syntax" ) : true ,
2023-02-17 17:37:03 +00:00
// Nothing is using edition yet.
descPkg . Append ( "FileDescriptorProto.edition" ) : true ,
2019-01-26 22:24:59 +00:00
2020-04-28 21:44:38 +00:00
// Impossible to test proto3 optional in a proto2 file.
descPkg . Append ( "FieldDescriptorProto.proto3_optional" ) : true ,
2019-01-26 22:24:59 +00:00
// TODO: Test oneof and extension options. Testing these requires extending the
// options messages (because they contain no user-settable fields), but importing
2022-05-18 00:29:33 +00:00
// descriptor.proto from test.proto currently causes an import cycle. Add test
2019-01-26 22:24:59 +00:00
// cases when that import cycle has been fixed.
2020-01-11 08:25:01 +00:00
descPkg . Append ( "OneofDescriptorProto.options" ) : true ,
2019-01-26 22:24:59 +00:00
}
for _ , messageName := range [ ] protoreflect . Name {
"FileDescriptorProto" ,
"DescriptorProto" ,
"FieldDescriptorProto" ,
"OneofDescriptorProto" ,
"EnumDescriptorProto" ,
"EnumValueDescriptorProto" ,
} {
2020-01-11 08:25:01 +00:00
message := descFile . Messages ( ) . ByName ( messageName )
2019-01-26 22:24:59 +00:00
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 )
}
}
}
2019-03-20 00:04:06 +00:00
// Verify that message descriptors for map entries have no Go type info.
mapEntryName := protoreflect . FullName ( "goproto.proto.test.TestAllTypes.MapInt32Int32Entry" )
2020-02-04 21:10:26 +00:00
d := testFile . Messages ( ) . ByName ( "TestAllTypes" ) . Fields ( ) . ByName ( "map_int32_int32" ) . Message ( )
reflect/protoreflect, reflect/protoregistry: move descriptor lookup to registry
Drop the protoreflect.FileDescriptor.DescriptorByName method.
Descriptor lookup will always happen through a protoregistry.Files, which
is more generally useful (it's rare that you want to find a descriptor in a
specific file, as opposed to a package which may be composed of multiple files).
Split protoregistry.Files descriptor lookup into individual per-type functions
(enum, message, extension, service), matching the preg.Types API.
Drop the ability to look up enum values, message fields, and service methods
for now. This can be easily added later if needed, and is trivial to implement
in user code. (e.g., look up the service and then consult sd.Methods.ByName().)
Change-Id: I2b3d8ef888921a8464ba1434eddab20c7d3a458e
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/172118
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-04-15 20:05:13 +00:00
if gotName , wantName := d . FullName ( ) , mapEntryName ; gotName != wantName {
t . Fatalf ( "looked up wrong descriptor: got %v, want %v" , gotName , wantName )
2019-03-20 00:04:06 +00:00
}
if _ , ok := d . ( protoreflect . MessageType ) ; ok {
t . Errorf ( "message descriptor for %v must not implement protoreflect.MessageType" , mapEntryName )
}
2019-01-26 22:24:59 +00:00
}
// visitFields calls f for every field set in m and its children.
func visitFields ( m protoreflect . Message , f func ( protoreflect . FieldDescriptor ) ) {
2019-04-26 06:48:08 +00:00
m . Range ( func ( fd protoreflect . FieldDescriptor , value protoreflect . Value ) bool {
f ( fd )
switch fd . Kind ( ) {
2019-01-26 22:24:59 +00:00
case protoreflect . MessageKind , protoreflect . GroupKind :
2019-04-26 06:48:08 +00:00
if fd . IsList ( ) {
2019-01-26 22:24:59 +00:00
for i , list := 0 , value . List ( ) ; i < list . Len ( ) ; i ++ {
visitFields ( list . Get ( i ) . Message ( ) , f )
}
} else {
visitFields ( value . Message ( ) , f )
}
}
return true
} )
}
2019-05-08 14:52:49 +00:00
func TestWeakInit ( t * testing . T ) {
2019-04-08 20:52:14 +00:00
// We do not expect to get a placeholder since weak1 is imported.
2020-02-04 21:10:26 +00:00
fd1 := testFile . Messages ( ) . ByName ( "TestWeak" ) . Fields ( ) . ByName ( "weak_message1" )
2019-04-08 20:52:14 +00:00
if got , want := fd1 . IsWeak ( ) , true ; got != want {
t . Errorf ( "field %v: IsWeak() = %v, want %v" , fd1 . FullName ( ) , got , want )
}
if got , want := fd1 . Message ( ) . IsPlaceholder ( ) , false ; got != want {
t . Errorf ( "field %v: Message.IsPlaceholder() = %v, want %v" , fd1 . FullName ( ) , got , want )
}
if got , want := fd1 . Message ( ) . Fields ( ) . Len ( ) , 1 ; got != want {
t . Errorf ( "field %v: Message().Fields().Len() == %d, want %d" , fd1 . FullName ( ) , got , want )
}
// We do expect to get a placeholder since weak2 is not imported.
2020-02-04 21:10:26 +00:00
fd2 := testFile . Messages ( ) . ByName ( "TestWeak" ) . Fields ( ) . ByName ( "weak_message2" )
2019-04-08 20:52:14 +00:00
if got , want := fd2 . IsWeak ( ) , true ; got != want {
t . Errorf ( "field %v: IsWeak() = %v, want %v" , fd2 . FullName ( ) , got , want )
2019-05-08 14:52:49 +00:00
}
2019-04-08 20:52:14 +00:00
if got , want := fd2 . Message ( ) . IsPlaceholder ( ) , true ; got != want {
t . Errorf ( "field %v: Message.IsPlaceholder() = %v, want %v" , fd2 . FullName ( ) , got , want )
2019-05-08 14:52:49 +00:00
}
2019-04-08 20:52:14 +00:00
if got , want := fd2 . Message ( ) . Fields ( ) . Len ( ) , 0 ; got != want {
t . Errorf ( "field %v: Message().Fields().Len() == %d, want %d" , fd2 . FullName ( ) , got , want )
2019-05-08 14:52:49 +00:00
}
}