internal/fileinit: add tests for fileinit and protodesc
Test the fileinit and protodesc packages by verifying that passing a
FileDescriptorProto through a round trip of these packages leaves it
unchanged.
Change-Id: I6bfb894d95f1736f9908adee1ab63e9653b3f1be
Reviewed-on: https://go-review.googlesource.com/c/159762
Reviewed-by: Herbie Ong <herbie@google.com>
diff --git a/internal/fileinit/fileinit_test.go b/internal/fileinit/fileinit_test.go
new file mode 100644
index 0000000..f2d0761
--- /dev/null
+++ b/internal/fileinit/fileinit_test.go
@@ -0,0 +1,92 @@
+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.Test_protoFile)
+
+ 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.Descriptor_protoFile.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)
+ }
+ }
+ }
+
+}
+
+// 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
+ })
+}