internal/filedesc, internal/filetype: initial commit
The internal/fileinit package is split apart into two packages:
* internal/filedesc constructs descriptors from the raw proto.
It is very similar to the previous internal/fileinit package.
* internal/filetype wraps descriptors with Go type information
Overview:
* The internal/fileinit package will be deleted in a future CL.
It is kept around since the v1 repo currently depends on it.
* The internal/prototype package is deleted. All former usages of it
are now using internal/filedesc instead. Most significantly,
the reflect/protodesc package was almost entirely re-written.
* The internal/impl package drops support for messages that do not
have a Descriptor method (pre-2016). This removes a significant amount
of technical debt.
filedesc.Builder to parse raw descriptors.
* The internal/encoding/defval package now handles enum values by name.
Change-Id: I3957bcc8588a70470fd6c7de1122216b80615ab7
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/182360
Reviewed-by: Damien Neil <dneil@google.com>
diff --git a/internal/filedesc/build_test.go b/internal/filedesc/build_test.go
new file mode 100644
index 0000000..edeb56e
--- /dev/null
+++ b/internal/filedesc/build_test.go
@@ -0,0 +1,117 @@
+// 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
+
+import (
+ "bytes"
+ "compress/gzip"
+ "io/ioutil"
+ "testing"
+
+ proto "github.com/golang/protobuf/proto"
+ "google.golang.org/protobuf/reflect/protodesc"
+ "google.golang.org/protobuf/reflect/protoreflect"
+
+ testpb "google.golang.org/protobuf/internal/testprotos/test"
+ "google.golang.org/protobuf/types/descriptorpb"
+)
+
+func TestInit(t *testing.T) {
+ // Compare the FileDescriptorProto for the same test file from two different sources:
+ //
+ // 1. The result of passing the filedesc-produced FileDescriptor through protodesc.
+ // 2. The protoc-generated wire-encoded message.
+ //
+ // This serves as a test of both filedesc 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)) {
+ m.Range(func(fd protoreflect.FieldDescriptor, value protoreflect.Value) bool {
+ f(fd)
+ switch fd.Kind() {
+ case protoreflect.MessageKind, protoreflect.GroupKind:
+ if fd.IsList() {
+ 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())
+ }
+}