github.com/bakjos/protoreflect@v1.9.2/dynamic/msgregistry/message_registry_test.go (about)

     1  package msgregistry
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"io/ioutil"
     7  	"os"
     8  	"reflect"
     9  	"strings"
    10  	"testing"
    11  
    12  	"github.com/golang/protobuf/proto"
    13  	"github.com/golang/protobuf/protoc-gen-go/descriptor"
    14  	"github.com/golang/protobuf/ptypes"
    15  	"github.com/golang/protobuf/ptypes/any"
    16  	"github.com/golang/protobuf/ptypes/duration"
    17  	"github.com/golang/protobuf/ptypes/wrappers"
    18  	"google.golang.org/genproto/protobuf/api"
    19  	"google.golang.org/genproto/protobuf/ptype"
    20  	"google.golang.org/genproto/protobuf/source_context"
    21  
    22  	"github.com/bakjos/protoreflect/desc"
    23  	"github.com/bakjos/protoreflect/desc/protoparse"
    24  	"github.com/bakjos/protoreflect/dynamic"
    25  	"github.com/bakjos/protoreflect/internal/testprotos"
    26  	"github.com/bakjos/protoreflect/internal/testutil"
    27  )
    28  
    29  func TestMessageRegistry_LookupTypes(t *testing.T) {
    30  	mr := &MessageRegistry{}
    31  
    32  	// register some types
    33  	md, err := desc.LoadMessageDescriptor("google.protobuf.DescriptorProto")
    34  	testutil.Ok(t, err)
    35  	err = mr.AddMessage("foo.bar/google.protobuf.DescriptorProto", md)
    36  	testutil.Ok(t, err)
    37  	ed := md.GetFile().FindEnum("google.protobuf.FieldDescriptorProto.Type")
    38  	testutil.Require(t, ed != nil)
    39  	err = mr.AddEnum("foo.bar/google.protobuf.FieldDescriptorProto.Type", ed)
    40  	testutil.Ok(t, err)
    41  
    42  	// lookups succeed
    43  	msg, err := mr.FindMessageTypeByUrl("foo.bar/google.protobuf.DescriptorProto")
    44  	testutil.Ok(t, err)
    45  	testutil.Eq(t, md, msg)
    46  	testutil.Eq(t, "https://foo.bar/google.protobuf.DescriptorProto", mr.ComputeURL(md))
    47  	en, err := mr.FindEnumTypeByUrl("foo.bar/google.protobuf.FieldDescriptorProto.Type")
    48  	testutil.Ok(t, err)
    49  	testutil.Eq(t, ed, en)
    50  	testutil.Eq(t, "https://foo.bar/google.protobuf.FieldDescriptorProto.Type", mr.ComputeURL(ed))
    51  
    52  	// right name but wrong domain? not found
    53  	msg, err = mr.FindMessageTypeByUrl("type.googleapis.com/google.protobuf.DescriptorProto")
    54  	testutil.Ok(t, err)
    55  	testutil.Require(t, msg == nil)
    56  	en, err = mr.FindEnumTypeByUrl("type.googleapis.com/google.protobuf.FieldDescriptorProto.Type")
    57  	testutil.Ok(t, err)
    58  	testutil.Require(t, en == nil)
    59  
    60  	// wrong type
    61  	_, err = mr.FindMessageTypeByUrl("foo.bar/google.protobuf.FieldDescriptorProto.Type")
    62  	_, ok := err.(*ErrUnexpectedType)
    63  	testutil.Require(t, ok)
    64  	_, err = mr.FindEnumTypeByUrl("foo.bar/google.protobuf.DescriptorProto")
    65  	_, ok = err.(*ErrUnexpectedType)
    66  	testutil.Require(t, ok)
    67  
    68  	// unmarshal any successfully finds the registered type
    69  	b, err := proto.Marshal(md.AsProto())
    70  	testutil.Ok(t, err)
    71  	a := &any.Any{TypeUrl: "foo.bar/google.protobuf.DescriptorProto", Value: b}
    72  	pm, err := mr.UnmarshalAny(a)
    73  	testutil.Ok(t, err)
    74  	testutil.Ceq(t, md.AsProto(), pm, eqm)
    75  	// we didn't configure the registry with a message factory, so it would have
    76  	// produced a dynamic message instead of a generated message
    77  	testutil.Eq(t, reflect.TypeOf((*dynamic.Message)(nil)), reflect.TypeOf(pm))
    78  
    79  	// by default, message registry knows about well-known types
    80  	dur := &duration.Duration{Nanos: 100, Seconds: 1000}
    81  	b, err = proto.Marshal(dur)
    82  	testutil.Ok(t, err)
    83  	a = &any.Any{TypeUrl: "foo.bar/google.protobuf.Duration", Value: b}
    84  	pm, err = mr.UnmarshalAny(a)
    85  	testutil.Ok(t, err)
    86  	testutil.Ceq(t, dur, pm, eqm)
    87  	testutil.Eq(t, reflect.TypeOf((*duration.Duration)(nil)), reflect.TypeOf(pm))
    88  
    89  	fd, err := desc.LoadFileDescriptor("desc_test1.proto")
    90  	testutil.Ok(t, err)
    91  	mr.AddFile("frob.nitz/foo.bar", fd)
    92  	msgCount, enumCount := 0, 0
    93  	mds := fd.GetMessageTypes()
    94  	for i := 0; i < len(mds); i++ {
    95  		md := mds[i]
    96  		msgCount++
    97  		mds = append(mds, md.GetNestedMessageTypes()...)
    98  		exp := fmt.Sprintf("https://frob.nitz/foo.bar/%s", md.GetFullyQualifiedName())
    99  		testutil.Eq(t, exp, mr.ComputeURL(md))
   100  		for _, ed := range md.GetNestedEnumTypes() {
   101  			enumCount++
   102  			exp := fmt.Sprintf("https://frob.nitz/foo.bar/%s", ed.GetFullyQualifiedName())
   103  			testutil.Eq(t, exp, mr.ComputeURL(ed))
   104  		}
   105  	}
   106  	for _, ed := range fd.GetEnumTypes() {
   107  		enumCount++
   108  		exp := fmt.Sprintf("https://frob.nitz/foo.bar/%s", ed.GetFullyQualifiedName())
   109  		testutil.Eq(t, exp, mr.ComputeURL(ed))
   110  	}
   111  	// sanity check
   112  	testutil.Eq(t, 11, msgCount)
   113  	testutil.Eq(t, 2, enumCount)
   114  }
   115  
   116  func TestMessageRegistry_LookupTypes_WithDefaults(t *testing.T) {
   117  	mr := NewMessageRegistryWithDefaults()
   118  
   119  	md, err := desc.LoadMessageDescriptor("google.protobuf.DescriptorProto")
   120  	testutil.Ok(t, err)
   121  	ed := md.GetFile().FindEnum("google.protobuf.FieldDescriptorProto.Type")
   122  	testutil.Require(t, ed != nil)
   123  
   124  	// lookups succeed
   125  	msg, err := mr.FindMessageTypeByUrl("type.googleapis.com/google.protobuf.DescriptorProto")
   126  	testutil.Ok(t, err)
   127  	testutil.Eq(t, md, msg)
   128  	// default types don't know their base URL, so will resolve even w/ wrong name
   129  	// (just have to get fully-qualified message name right)
   130  	msg, err = mr.FindMessageTypeByUrl("foo.bar/google.protobuf.DescriptorProto")
   131  	testutil.Ok(t, err)
   132  	testutil.Eq(t, md, msg)
   133  
   134  	// sad trombone: no way to lookup "default" enum types, so enums don't resolve
   135  	// without being explicitly registered :(
   136  	en, err := mr.FindEnumTypeByUrl("type.googleapis.com/google.protobuf.FieldDescriptorProto.Type")
   137  	testutil.Ok(t, err)
   138  	testutil.Require(t, en == nil)
   139  	en, err = mr.FindEnumTypeByUrl("foo.bar/google.protobuf.FieldDescriptorProto.Type")
   140  	testutil.Ok(t, err)
   141  	testutil.Require(t, en == nil)
   142  
   143  	// unmarshal any successfully finds the registered type
   144  	b, err := proto.Marshal(md.AsProto())
   145  	testutil.Ok(t, err)
   146  	a := &any.Any{TypeUrl: "foo.bar/google.protobuf.DescriptorProto", Value: b}
   147  	pm, err := mr.UnmarshalAny(a)
   148  	testutil.Ok(t, err)
   149  	testutil.Ceq(t, md.AsProto(), pm, eqm)
   150  	// message registry with defaults implies known-type registry with defaults, so
   151  	// it should have marshalled the message into a generated message
   152  	testutil.Eq(t, reflect.TypeOf((*descriptor.DescriptorProto)(nil)), reflect.TypeOf(pm))
   153  }
   154  
   155  func TestMessageRegistry_FindMessage_WithFetcher(t *testing.T) {
   156  	tf := createFetcher(t)
   157  	// we want "defaults" for the message factory so that we can properly process
   158  	// known extensions (which the type fetcher puts into the descriptor options)
   159  	mr := (&MessageRegistry{}).WithFetcher(tf).WithMessageFactory(dynamic.NewMessageFactoryWithDefaults())
   160  
   161  	md, err := mr.FindMessageTypeByUrl("foo.bar/some.Type")
   162  	testutil.Ok(t, err)
   163  
   164  	// Fairly in-depth check of the returned message descriptor:
   165  
   166  	testutil.Eq(t, "Type", md.GetName())
   167  	testutil.Eq(t, "some.Type", md.GetFullyQualifiedName())
   168  	testutil.Eq(t, "some", md.GetFile().GetPackage())
   169  	testutil.Eq(t, true, md.GetFile().IsProto3())
   170  	testutil.Eq(t, true, md.IsProto3())
   171  
   172  	mo := &descriptor.MessageOptions{
   173  		Deprecated: proto.Bool(true),
   174  	}
   175  	err = proto.SetExtension(mo, testprotos.E_Mfubar, proto.Bool(true))
   176  	testutil.Ok(t, err)
   177  	testutil.Ceq(t, mo, md.GetMessageOptions(), eqpm)
   178  
   179  	flds := md.GetFields()
   180  	testutil.Eq(t, 4, len(flds))
   181  	testutil.Eq(t, "a", flds[0].GetName())
   182  	testutil.Eq(t, int32(1), flds[0].GetNumber())
   183  	testutil.Eq(t, (*desc.OneOfDescriptor)(nil), flds[0].GetOneOf())
   184  	testutil.Eq(t, descriptor.FieldDescriptorProto_LABEL_OPTIONAL, flds[0].GetLabel())
   185  	testutil.Eq(t, descriptor.FieldDescriptorProto_TYPE_MESSAGE, flds[0].GetType())
   186  
   187  	fo := &descriptor.FieldOptions{
   188  		Deprecated: proto.Bool(true),
   189  	}
   190  	err = proto.SetExtension(fo, testprotos.E_Ffubar, []string{"foo", "bar", "baz"})
   191  	testutil.Ok(t, err)
   192  	err = proto.SetExtension(fo, testprotos.E_Ffubarb, []byte{1, 2, 3, 4, 5, 6, 7, 8})
   193  	testutil.Ok(t, err)
   194  	testutil.Ceq(t, fo, flds[0].GetFieldOptions(), eqpm)
   195  
   196  	testutil.Eq(t, "b", flds[1].GetName())
   197  	testutil.Eq(t, int32(2), flds[1].GetNumber())
   198  	testutil.Eq(t, (*desc.OneOfDescriptor)(nil), flds[1].GetOneOf())
   199  	testutil.Eq(t, descriptor.FieldDescriptorProto_LABEL_REPEATED, flds[1].GetLabel())
   200  	testutil.Eq(t, descriptor.FieldDescriptorProto_TYPE_STRING, flds[1].GetType())
   201  
   202  	testutil.Eq(t, "c", flds[2].GetName())
   203  	testutil.Eq(t, int32(3), flds[2].GetNumber())
   204  	testutil.Eq(t, "un", flds[2].GetOneOf().GetName())
   205  	testutil.Eq(t, descriptor.FieldDescriptorProto_LABEL_OPTIONAL, flds[2].GetLabel())
   206  	testutil.Eq(t, descriptor.FieldDescriptorProto_TYPE_ENUM, flds[2].GetType())
   207  
   208  	testutil.Eq(t, "d", flds[3].GetName())
   209  	testutil.Eq(t, int32(4), flds[3].GetNumber())
   210  	testutil.Eq(t, "un", flds[3].GetOneOf().GetName())
   211  	testutil.Eq(t, descriptor.FieldDescriptorProto_LABEL_OPTIONAL, flds[3].GetLabel())
   212  	testutil.Eq(t, descriptor.FieldDescriptorProto_TYPE_INT32, flds[3].GetType())
   213  
   214  	oos := md.GetOneOfs()
   215  	testutil.Eq(t, 1, len(oos))
   216  	testutil.Eq(t, "un", oos[0].GetName())
   217  	ooflds := oos[0].GetChoices()
   218  	testutil.Eq(t, 2, len(ooflds))
   219  	testutil.Eq(t, flds[2], ooflds[0])
   220  	testutil.Eq(t, flds[3], ooflds[1])
   221  
   222  	// Quick, shallow check of the linked descriptors:
   223  
   224  	md2 := md.FindFieldByName("a").GetMessageType()
   225  	testutil.Eq(t, "OtherType", md2.GetName())
   226  	testutil.Eq(t, "some.OtherType", md2.GetFullyQualifiedName())
   227  	testutil.Eq(t, "some", md2.GetFile().GetPackage())
   228  	testutil.Eq(t, false, md2.GetFile().IsProto3())
   229  	testutil.Eq(t, false, md2.IsProto3())
   230  
   231  	nmd := md2.GetNestedMessageTypes()[0]
   232  	testutil.Ceq(t, nmd.AsProto(), md2.FindFieldByName("a").GetMessageType().AsProto(), eqpm)
   233  	testutil.Eq(t, "AnotherType", nmd.GetName())
   234  	testutil.Eq(t, "some.OtherType.AnotherType", nmd.GetFullyQualifiedName())
   235  	testutil.Eq(t, "some", nmd.GetFile().GetPackage())
   236  	testutil.Eq(t, false, nmd.GetFile().IsProto3())
   237  	testutil.Eq(t, false, nmd.IsProto3())
   238  
   239  	en := md.FindFieldByName("c").GetEnumType()
   240  	testutil.Eq(t, "Enum", en.GetName())
   241  	testutil.Eq(t, "some.Enum", en.GetFullyQualifiedName())
   242  	testutil.Eq(t, "some", en.GetFile().GetPackage())
   243  	testutil.Eq(t, true, en.GetFile().IsProto3())
   244  
   245  	// Ask for another one. This one has a name that looks like "some.YetAnother"
   246  	// package in this context.
   247  	md3, err := mr.FindMessageTypeByUrl("foo.bar/some.YetAnother.MessageType")
   248  	testutil.Ok(t, err)
   249  	testutil.Eq(t, "MessageType", md3.GetName())
   250  	testutil.Eq(t, "some.YetAnother.MessageType", md3.GetFullyQualifiedName())
   251  	testutil.Eq(t, "some.YetAnother", md3.GetFile().GetPackage())
   252  	testutil.Eq(t, false, md3.GetFile().IsProto3())
   253  	testutil.Eq(t, false, md3.IsProto3())
   254  }
   255  
   256  func TestMessageRegistry_FindMessage_Mixed(t *testing.T) {
   257  	msgType := &ptype.Type{
   258  		Name:   "foo.Bar",
   259  		Oneofs: []string{"baz"},
   260  		Fields: []*ptype.Field{
   261  			{
   262  				Name:        "id",
   263  				Number:      1,
   264  				Kind:        ptype.Field_TYPE_UINT64,
   265  				Cardinality: ptype.Field_CARDINALITY_OPTIONAL,
   266  				JsonName:    "id",
   267  			},
   268  			{
   269  				Name:        "name",
   270  				Number:      2,
   271  				Kind:        ptype.Field_TYPE_STRING,
   272  				Cardinality: ptype.Field_CARDINALITY_OPTIONAL,
   273  				JsonName:    "name",
   274  			},
   275  			{
   276  				Name:        "count",
   277  				Number:      3,
   278  				OneofIndex:  1,
   279  				Kind:        ptype.Field_TYPE_INT32,
   280  				Cardinality: ptype.Field_CARDINALITY_OPTIONAL,
   281  				JsonName:    "count",
   282  			},
   283  			{
   284  				Name:        "data",
   285  				Number:      4,
   286  				OneofIndex:  1,
   287  				Kind:        ptype.Field_TYPE_BYTES,
   288  				Cardinality: ptype.Field_CARDINALITY_OPTIONAL,
   289  				JsonName:    "data",
   290  			},
   291  			{
   292  				Name:        "other",
   293  				Number:      5,
   294  				OneofIndex:  1,
   295  				Kind:        ptype.Field_TYPE_MESSAGE,
   296  				Cardinality: ptype.Field_CARDINALITY_OPTIONAL,
   297  				JsonName:    "other",
   298  				TypeUrl:     "type.googleapis.com/google.protobuf.Empty",
   299  			},
   300  			{
   301  				Name:        "created",
   302  				Number:      6,
   303  				Kind:        ptype.Field_TYPE_MESSAGE,
   304  				Cardinality: ptype.Field_CARDINALITY_OPTIONAL,
   305  				JsonName:    "created",
   306  				TypeUrl:     "type.googleapis.com/google.protobuf.Timestamp",
   307  			},
   308  			{
   309  				Name:        "updated",
   310  				Number:      7,
   311  				Kind:        ptype.Field_TYPE_MESSAGE,
   312  				Cardinality: ptype.Field_CARDINALITY_OPTIONAL,
   313  				JsonName:    "updated",
   314  				TypeUrl:     "type.googleapis.com/google.protobuf.Timestamp",
   315  			},
   316  			{
   317  				Name:        "tombstone",
   318  				Number:      8,
   319  				Kind:        ptype.Field_TYPE_BOOL,
   320  				Cardinality: ptype.Field_CARDINALITY_OPTIONAL,
   321  				JsonName:    "tombstone",
   322  			},
   323  		},
   324  		SourceContext: &source_context.SourceContext{
   325  			FileName: "test/foo.proto",
   326  		},
   327  		Syntax: ptype.Syntax_SYNTAX_PROTO3,
   328  	}
   329  
   330  	var mr MessageRegistry
   331  	mr.WithFetcher(func(url string, enum bool) (proto.Message, error) {
   332  		if url == "https://foo.test.com/foo.Bar" && !enum {
   333  			return msgType, nil
   334  		}
   335  		return nil, fmt.Errorf("unknown type: %s", url)
   336  	})
   337  
   338  	// Make sure we successfully get back a descriptor
   339  	md, err := mr.FindMessageTypeByUrl("foo.test.com/foo.Bar")
   340  	testutil.Ok(t, err)
   341  
   342  	// Check its properties. It should have the fields from the type
   343  	// description above, but also correctly refer to google/protobuf
   344  	// dependencies (which came from resolver, not the fetcher).
   345  
   346  	testutil.Eq(t, "foo.Bar", md.GetFullyQualifiedName())
   347  	testutil.Eq(t, "Bar", md.GetName())
   348  	testutil.Eq(t, "test/foo.proto", md.GetFile().GetName())
   349  	testutil.Eq(t, "foo", md.GetFile().GetPackage())
   350  
   351  	fd := md.FindFieldByName("created")
   352  	testutil.Eq(t, "google.protobuf.Timestamp", fd.GetMessageType().GetFullyQualifiedName())
   353  	testutil.Eq(t, "google/protobuf/timestamp.proto", fd.GetMessageType().GetFile().GetName())
   354  
   355  	ood := md.GetOneOfs()[0]
   356  	testutil.Eq(t, 3, len(ood.GetChoices()))
   357  	fd = ood.GetChoices()[2]
   358  	testutil.Eq(t, "google.protobuf.Empty", fd.GetMessageType().GetFullyQualifiedName())
   359  	testutil.Eq(t, "google/protobuf/empty.proto", fd.GetMessageType().GetFile().GetName())
   360  }
   361  
   362  func TestMessageRegistry_FindEnum_WithFetcher(t *testing.T) {
   363  	tf := createFetcher(t)
   364  	// we want "defaults" for the message factory so that we can properly process
   365  	// known extensions (which the type fetcher puts into the descriptor options)
   366  	mr := (&MessageRegistry{}).WithFetcher(tf).WithMessageFactory(dynamic.NewMessageFactoryWithDefaults())
   367  
   368  	ed, err := mr.FindEnumTypeByUrl("foo.bar/some.Enum")
   369  	testutil.Ok(t, err)
   370  
   371  	testutil.Eq(t, "Enum", ed.GetName())
   372  	testutil.Eq(t, "some.Enum", ed.GetFullyQualifiedName())
   373  	testutil.Eq(t, "some", ed.GetFile().GetPackage())
   374  	testutil.Eq(t, true, ed.GetFile().IsProto3())
   375  
   376  	eo := &descriptor.EnumOptions{
   377  		Deprecated: proto.Bool(true),
   378  		AllowAlias: proto.Bool(true),
   379  	}
   380  	err = proto.SetExtension(eo, testprotos.E_Efubar, proto.Int32(-42))
   381  	testutil.Ok(t, err)
   382  	err = proto.SetExtension(eo, testprotos.E_Efubars, proto.Int32(-42))
   383  	testutil.Ok(t, err)
   384  	err = proto.SetExtension(eo, testprotos.E_Efubarsf, proto.Int32(-42))
   385  	testutil.Ok(t, err)
   386  	err = proto.SetExtension(eo, testprotos.E_Efubaru, proto.Uint32(42))
   387  	testutil.Ok(t, err)
   388  	err = proto.SetExtension(eo, testprotos.E_Efubaruf, proto.Uint32(42))
   389  	testutil.Ok(t, err)
   390  	testutil.Ceq(t, eo, ed.GetEnumOptions(), eqpm)
   391  
   392  	vals := ed.GetValues()
   393  	testutil.Eq(t, 2, len(vals))
   394  	testutil.Eq(t, "ABC", vals[0].GetName())
   395  	testutil.Eq(t, int32(1), vals[0].GetNumber())
   396  
   397  	evo := &descriptor.EnumValueOptions{
   398  		Deprecated: proto.Bool(true),
   399  	}
   400  	err = proto.SetExtension(evo, testprotos.E_Evfubar, proto.Int64(-420420420420))
   401  	testutil.Ok(t, err)
   402  	err = proto.SetExtension(evo, testprotos.E_Evfubars, proto.Int64(-420420420420))
   403  	testutil.Ok(t, err)
   404  	err = proto.SetExtension(evo, testprotos.E_Evfubarsf, proto.Int64(-420420420420))
   405  	testutil.Ok(t, err)
   406  	err = proto.SetExtension(evo, testprotos.E_Evfubaru, proto.Uint64(420420420420))
   407  	testutil.Ok(t, err)
   408  	err = proto.SetExtension(evo, testprotos.E_Evfubaruf, proto.Uint64(420420420420))
   409  	testutil.Ok(t, err)
   410  	testutil.Ceq(t, evo, vals[0].GetEnumValueOptions(), eqpm)
   411  
   412  	testutil.Eq(t, "XYZ", vals[1].GetName())
   413  	testutil.Eq(t, int32(2), vals[1].GetNumber())
   414  }
   415  
   416  func createFetcher(t *testing.T) TypeFetcher {
   417  	bol, err := ptypes.MarshalAny(&wrappers.BoolValue{Value: true})
   418  	testutil.Ok(t, err)
   419  	in32, err := ptypes.MarshalAny(&wrappers.Int32Value{Value: -42})
   420  	testutil.Ok(t, err)
   421  	uin32, err := ptypes.MarshalAny(&wrappers.UInt32Value{Value: 42})
   422  	testutil.Ok(t, err)
   423  	in64, err := ptypes.MarshalAny(&wrappers.Int64Value{Value: -420420420420})
   424  	testutil.Ok(t, err)
   425  	uin64, err := ptypes.MarshalAny(&wrappers.UInt64Value{Value: 420420420420})
   426  	testutil.Ok(t, err)
   427  	byt, err := ptypes.MarshalAny(&wrappers.BytesValue{Value: []byte{1, 2, 3, 4, 5, 6, 7, 8}})
   428  	testutil.Ok(t, err)
   429  	str1, err := ptypes.MarshalAny(&wrappers.StringValue{Value: "foo"})
   430  	testutil.Ok(t, err)
   431  	str2, err := ptypes.MarshalAny(&wrappers.StringValue{Value: "bar"})
   432  	testutil.Ok(t, err)
   433  	str3, err := ptypes.MarshalAny(&wrappers.StringValue{Value: "baz"})
   434  	testutil.Ok(t, err)
   435  
   436  	types := map[string]proto.Message{
   437  		"https://foo.bar/some.Type": &ptype.Type{
   438  			Name:   "some.Type",
   439  			Oneofs: []string{"un"},
   440  			Fields: []*ptype.Field{
   441  				{
   442  					Name:        "a",
   443  					JsonName:    "a",
   444  					Number:      1,
   445  					Cardinality: ptype.Field_CARDINALITY_OPTIONAL,
   446  					Kind:        ptype.Field_TYPE_MESSAGE,
   447  					TypeUrl:     "foo.bar/some.OtherType",
   448  					Options: []*ptype.Option{
   449  						{
   450  							Name:  "deprecated",
   451  							Value: bol,
   452  						},
   453  						{
   454  							Name:  "testprotos.ffubar",
   455  							Value: str1,
   456  						},
   457  						{
   458  							Name:  "testprotos.ffubar",
   459  							Value: str2,
   460  						},
   461  						{
   462  							Name:  "testprotos.ffubar",
   463  							Value: str3,
   464  						},
   465  						{
   466  							Name:  "testprotos.ffubarb",
   467  							Value: byt,
   468  						},
   469  					},
   470  				},
   471  				{
   472  					Name:        "b",
   473  					JsonName:    "b",
   474  					Number:      2,
   475  					Cardinality: ptype.Field_CARDINALITY_REPEATED,
   476  					Kind:        ptype.Field_TYPE_STRING,
   477  				},
   478  				{
   479  					Name:        "c",
   480  					JsonName:    "c",
   481  					Number:      3,
   482  					Cardinality: ptype.Field_CARDINALITY_OPTIONAL,
   483  					Kind:        ptype.Field_TYPE_ENUM,
   484  					TypeUrl:     "foo.bar/some.Enum",
   485  					OneofIndex:  1,
   486  				},
   487  				{
   488  					Name:        "d",
   489  					JsonName:    "d",
   490  					Number:      4,
   491  					Cardinality: ptype.Field_CARDINALITY_OPTIONAL,
   492  					Kind:        ptype.Field_TYPE_INT32,
   493  					OneofIndex:  1,
   494  				},
   495  			},
   496  			Options: []*ptype.Option{
   497  				{
   498  					Name:  "deprecated",
   499  					Value: bol,
   500  				},
   501  				{
   502  					Name:  "testprotos.mfubar",
   503  					Value: bol,
   504  				},
   505  			},
   506  			SourceContext: &source_context.SourceContext{FileName: "foo.proto"},
   507  			Syntax:        ptype.Syntax_SYNTAX_PROTO3,
   508  		},
   509  		"https://foo.bar/some.OtherType": &ptype.Type{
   510  			Name: "some.OtherType",
   511  			Fields: []*ptype.Field{
   512  				{
   513  					Name:        "a",
   514  					JsonName:    "a",
   515  					Number:      1,
   516  					Cardinality: ptype.Field_CARDINALITY_OPTIONAL,
   517  					Kind:        ptype.Field_TYPE_MESSAGE,
   518  					TypeUrl:     "foo.bar/some.OtherType.AnotherType",
   519  				},
   520  			},
   521  			SourceContext: &source_context.SourceContext{FileName: "bar.proto"},
   522  			Syntax:        ptype.Syntax_SYNTAX_PROTO2,
   523  		},
   524  		"https://foo.bar/some.OtherType.AnotherType": &ptype.Type{
   525  			Name: "some.OtherType.AnotherType",
   526  			Fields: []*ptype.Field{
   527  				{
   528  					Name:        "a",
   529  					JsonName:    "a",
   530  					Number:      1,
   531  					Cardinality: ptype.Field_CARDINALITY_OPTIONAL,
   532  					Kind:        ptype.Field_TYPE_BYTES,
   533  				},
   534  			},
   535  			SourceContext: &source_context.SourceContext{FileName: "bar.proto"},
   536  			Syntax:        ptype.Syntax_SYNTAX_PROTO2,
   537  		},
   538  		"https://foo.bar/some.Enum": &ptype.Enum{
   539  			Name: "some.Enum",
   540  			Enumvalue: []*ptype.EnumValue{
   541  				{
   542  					Name:   "ABC",
   543  					Number: 1,
   544  					Options: []*ptype.Option{
   545  						{
   546  							Name:  "deprecated",
   547  							Value: bol,
   548  						},
   549  						{
   550  							Name:  "testprotos.evfubar",
   551  							Value: in64,
   552  						},
   553  						{
   554  							Name:  "testprotos.evfubars",
   555  							Value: in64,
   556  						},
   557  						{
   558  							Name:  "testprotos.evfubarsf",
   559  							Value: in64,
   560  						},
   561  						{
   562  							Name:  "testprotos.evfubaru",
   563  							Value: uin64,
   564  						},
   565  						{
   566  							Name:  "testprotos.evfubaruf",
   567  							Value: uin64,
   568  						},
   569  					},
   570  				},
   571  				{
   572  					Name:   "XYZ",
   573  					Number: 2,
   574  				},
   575  			},
   576  			Options: []*ptype.Option{
   577  				{
   578  					Name:  "deprecated",
   579  					Value: bol,
   580  				},
   581  				{
   582  					Name:  "allow_alias",
   583  					Value: bol,
   584  				},
   585  				{
   586  					Name:  "testprotos.efubar",
   587  					Value: in32,
   588  				},
   589  				{
   590  					Name:  "testprotos.efubars",
   591  					Value: in32,
   592  				},
   593  				{
   594  					Name:  "testprotos.efubarsf",
   595  					Value: in32,
   596  				},
   597  				{
   598  					Name:  "testprotos.efubaru",
   599  					Value: uin32,
   600  				},
   601  				{
   602  					Name:  "testprotos.efubaruf",
   603  					Value: uin32,
   604  				},
   605  			},
   606  			SourceContext: &source_context.SourceContext{FileName: "foo.proto"},
   607  			Syntax:        ptype.Syntax_SYNTAX_PROTO3,
   608  		},
   609  		"https://foo.bar/some.YetAnother.MessageType": &ptype.Type{
   610  			// in a separate file, so it will look like package some.YetAnother
   611  			Name: "some.YetAnother.MessageType",
   612  			Fields: []*ptype.Field{
   613  				{
   614  					Name:        "a",
   615  					JsonName:    "a",
   616  					Number:      1,
   617  					Cardinality: ptype.Field_CARDINALITY_OPTIONAL,
   618  					Kind:        ptype.Field_TYPE_STRING,
   619  				},
   620  			},
   621  			SourceContext: &source_context.SourceContext{FileName: "baz.proto"},
   622  			Syntax:        ptype.Syntax_SYNTAX_PROTO2,
   623  		},
   624  	}
   625  	return func(url string, enum bool) (proto.Message, error) {
   626  		t := types[url]
   627  		if t == nil {
   628  			return nil, nil
   629  		}
   630  		if _, ok := t.(*ptype.Enum); ok == enum {
   631  			return t, nil
   632  		} else {
   633  			return nil, fmt.Errorf("bad type for %s", url)
   634  		}
   635  	}
   636  }
   637  
   638  func TestMessageRegistry_ResolveApiIntoServiceDescriptor(t *testing.T) {
   639  	tf := createFetcher(t)
   640  	// we want "defaults" for the message factory so that we can properly process
   641  	// known extensions (which the type fetcher puts into the descriptor options)
   642  	mr := (&MessageRegistry{}).WithFetcher(tf).WithMessageFactory(dynamic.NewMessageFactoryWithDefaults())
   643  
   644  	sd, err := mr.ResolveApiIntoServiceDescriptor(getApi(t))
   645  	testutil.Ok(t, err)
   646  
   647  	testutil.Eq(t, "Service", sd.GetName())
   648  	testutil.Eq(t, "some.Service", sd.GetFullyQualifiedName())
   649  	testutil.Eq(t, "some", sd.GetFile().GetPackage())
   650  	testutil.Eq(t, true, sd.GetFile().IsProto3())
   651  
   652  	so := &descriptor.ServiceOptions{
   653  		Deprecated: proto.Bool(true),
   654  	}
   655  	err = proto.SetExtension(so, testprotos.E_Sfubar, &testprotos.ReallySimpleMessage{Id: proto.Uint64(100), Name: proto.String("deuce")})
   656  	testutil.Ok(t, err)
   657  	err = proto.SetExtension(so, testprotos.E_Sfubare, testprotos.ReallySimpleEnum_VALUE.Enum())
   658  	testutil.Ok(t, err)
   659  	testutil.Ceq(t, so, sd.GetServiceOptions(), eqpm)
   660  
   661  	methods := sd.GetMethods()
   662  	testutil.Eq(t, 4, len(methods))
   663  	testutil.Eq(t, "UnaryMethod", methods[0].GetName())
   664  	testutil.Eq(t, "some.Type", methods[0].GetInputType().GetFullyQualifiedName())
   665  	testutil.Eq(t, "some.OtherType", methods[0].GetOutputType().GetFullyQualifiedName())
   666  
   667  	mto := &descriptor.MethodOptions{
   668  		Deprecated: proto.Bool(true),
   669  	}
   670  	err = proto.SetExtension(mto, testprotos.E_Mtfubar, []float32{3.14159, 2.71828})
   671  	testutil.Ok(t, err)
   672  	err = proto.SetExtension(mto, testprotos.E_Mtfubard, proto.Float64(10203040.506070809))
   673  	testutil.Ok(t, err)
   674  	testutil.Ceq(t, mto, methods[0].GetMethodOptions(), eqpm)
   675  
   676  	testutil.Eq(t, "ClientStreamMethod", methods[1].GetName())
   677  	testutil.Eq(t, "some.OtherType", methods[1].GetInputType().GetFullyQualifiedName())
   678  	testutil.Eq(t, "some.Type", methods[1].GetOutputType().GetFullyQualifiedName())
   679  
   680  	testutil.Eq(t, "ServerStreamMethod", methods[2].GetName())
   681  	testutil.Eq(t, "some.OtherType.AnotherType", methods[2].GetInputType().GetFullyQualifiedName())
   682  	testutil.Eq(t, "some.YetAnother.MessageType", methods[2].GetOutputType().GetFullyQualifiedName())
   683  
   684  	testutil.Eq(t, "BidiStreamMethod", methods[3].GetName())
   685  	testutil.Eq(t, "some.YetAnother.MessageType", methods[3].GetInputType().GetFullyQualifiedName())
   686  	testutil.Eq(t, "some.OtherType.AnotherType", methods[3].GetOutputType().GetFullyQualifiedName())
   687  
   688  	// check linked message types
   689  
   690  	testutil.Eq(t, methods[0].GetInputType(), methods[1].GetOutputType())
   691  	testutil.Eq(t, methods[0].GetOutputType(), methods[1].GetInputType())
   692  	testutil.Eq(t, methods[2].GetInputType(), methods[3].GetOutputType())
   693  	testutil.Eq(t, methods[2].GetOutputType(), methods[3].GetInputType())
   694  
   695  	md1 := methods[0].GetInputType()
   696  	md2 := methods[0].GetOutputType()
   697  	md3 := methods[2].GetInputType()
   698  	md4 := methods[2].GetOutputType()
   699  
   700  	testutil.Eq(t, "Type", md1.GetName())
   701  	testutil.Eq(t, "some.Type", md1.GetFullyQualifiedName())
   702  	testutil.Eq(t, "some", md1.GetFile().GetPackage())
   703  	testutil.Eq(t, true, md1.GetFile().IsProto3())
   704  	testutil.Eq(t, true, md1.IsProto3())
   705  
   706  	testutil.Eq(t, "OtherType", md2.GetName())
   707  	testutil.Eq(t, "some.OtherType", md2.GetFullyQualifiedName())
   708  	testutil.Eq(t, "some", md2.GetFile().GetPackage())
   709  	testutil.Eq(t, false, md2.GetFile().IsProto3())
   710  	testutil.Eq(t, false, md2.IsProto3())
   711  
   712  	testutil.Eq(t, md3, md2.GetNestedMessageTypes()[0])
   713  	testutil.Eq(t, "AnotherType", md3.GetName())
   714  	testutil.Eq(t, "some.OtherType.AnotherType", md3.GetFullyQualifiedName())
   715  	testutil.Eq(t, "some", md3.GetFile().GetPackage())
   716  	testutil.Eq(t, false, md3.GetFile().IsProto3())
   717  	testutil.Eq(t, false, md3.IsProto3())
   718  
   719  	testutil.Eq(t, "MessageType", md4.GetName())
   720  	testutil.Eq(t, "some.YetAnother.MessageType", md4.GetFullyQualifiedName())
   721  	testutil.Eq(t, "some", md4.GetFile().GetPackage())
   722  	testutil.Eq(t, true, md4.GetFile().IsProto3())
   723  	testutil.Eq(t, true, md4.IsProto3())
   724  }
   725  
   726  func getApi(t *testing.T) *api.Api {
   727  	bol, err := ptypes.MarshalAny(&wrappers.BoolValue{Value: true})
   728  	testutil.Ok(t, err)
   729  	dbl, err := ptypes.MarshalAny(&wrappers.DoubleValue{Value: 10203040.506070809})
   730  	testutil.Ok(t, err)
   731  	flt1, err := ptypes.MarshalAny(&wrappers.FloatValue{Value: 3.14159})
   732  	testutil.Ok(t, err)
   733  	flt2, err := ptypes.MarshalAny(&wrappers.FloatValue{Value: 2.71828})
   734  	testutil.Ok(t, err)
   735  	enu, err := ptypes.MarshalAny(&wrappers.Int32Value{Value: int32(testprotos.ReallySimpleEnum_VALUE)})
   736  	testutil.Ok(t, err)
   737  	msg, err := ptypes.MarshalAny(&testprotos.ReallySimpleMessage{Id: proto.Uint64(100), Name: proto.String("deuce")})
   738  	testutil.Ok(t, err)
   739  	return &api.Api{
   740  		Name: "some.Service",
   741  		Methods: []*api.Method{
   742  			{
   743  				Name:            "UnaryMethod",
   744  				RequestTypeUrl:  "foo.bar/some.Type",
   745  				ResponseTypeUrl: "foo.bar/some.OtherType",
   746  				Options: []*ptype.Option{
   747  					{
   748  						Name:  "deprecated",
   749  						Value: bol,
   750  					},
   751  					{
   752  						Name:  "testprotos.mtfubar",
   753  						Value: flt1,
   754  					},
   755  					{
   756  						Name:  "testprotos.mtfubar",
   757  						Value: flt2,
   758  					},
   759  					{
   760  						Name:  "testprotos.mtfubard",
   761  						Value: dbl,
   762  					},
   763  				},
   764  				Syntax: ptype.Syntax_SYNTAX_PROTO3,
   765  			},
   766  			{
   767  				Name:             "ClientStreamMethod",
   768  				RequestStreaming: true,
   769  				RequestTypeUrl:   "foo.bar/some.OtherType",
   770  				ResponseTypeUrl:  "foo.bar/some.Type",
   771  				Syntax:           ptype.Syntax_SYNTAX_PROTO3,
   772  			},
   773  			{
   774  				Name:              "ServerStreamMethod",
   775  				ResponseStreaming: true,
   776  				RequestTypeUrl:    "foo.bar/some.OtherType.AnotherType",
   777  				ResponseTypeUrl:   "foo.bar/some.YetAnother.MessageType",
   778  				Syntax:            ptype.Syntax_SYNTAX_PROTO3,
   779  			},
   780  			{
   781  				Name:              "BidiStreamMethod",
   782  				RequestStreaming:  true,
   783  				ResponseStreaming: true,
   784  				RequestTypeUrl:    "foo.bar/some.YetAnother.MessageType",
   785  				ResponseTypeUrl:   "foo.bar/some.OtherType.AnotherType",
   786  				Syntax:            ptype.Syntax_SYNTAX_PROTO3,
   787  			},
   788  		},
   789  		Options: []*ptype.Option{
   790  			{
   791  				Name:  "deprecated",
   792  				Value: bol,
   793  			},
   794  			{
   795  				Name:  "testprotos.sfubar",
   796  				Value: msg,
   797  			},
   798  			{
   799  				Name:  "testprotos.sfubare",
   800  				Value: enu,
   801  			},
   802  		},
   803  		SourceContext: &source_context.SourceContext{FileName: "baz.proto"},
   804  		Syntax:        ptype.Syntax_SYNTAX_PROTO3,
   805  	}
   806  }
   807  
   808  func TestMessageRegistry_MarshalAndUnmarshalAny(t *testing.T) {
   809  	mr := NewMessageRegistryWithDefaults()
   810  
   811  	md, err := desc.LoadMessageDescriptor("google.protobuf.DescriptorProto")
   812  	testutil.Ok(t, err)
   813  
   814  	// marshal with default base URL
   815  	a, err := mr.MarshalAny(md.AsProto())
   816  	testutil.Ok(t, err)
   817  	testutil.Eq(t, "type.googleapis.com/google.protobuf.DescriptorProto", a.TypeUrl)
   818  
   819  	// check that we can unmarshal it with normal ptypes library
   820  	var umd descriptor.DescriptorProto
   821  	err = ptypes.UnmarshalAny(a, &umd)
   822  	testutil.Ok(t, err)
   823  	testutil.Ceq(t, md.AsProto(), &umd, eqpm)
   824  
   825  	// and that we can unmarshal it with a message registry
   826  	pm, err := mr.UnmarshalAny(a)
   827  	testutil.Ok(t, err)
   828  	_, ok := pm.(*descriptor.DescriptorProto)
   829  	testutil.Require(t, ok)
   830  	testutil.Ceq(t, md.AsProto(), pm, eqpm)
   831  
   832  	// and that we can unmarshal it as a dynamic message, using a
   833  	// message registry that doesn't know about the generated type
   834  	mrWithoutDefaults := &MessageRegistry{}
   835  	err = mrWithoutDefaults.AddMessage("type.googleapis.com/google.protobuf.DescriptorProto", md)
   836  	testutil.Ok(t, err)
   837  	pm, err = mrWithoutDefaults.UnmarshalAny(a)
   838  	testutil.Ok(t, err)
   839  	dm, ok := pm.(*dynamic.Message)
   840  	testutil.Require(t, ok)
   841  	testutil.Ceq(t, md.AsProto(), dm, eqm)
   842  
   843  	// now test generation of type URLs with other settings
   844  
   845  	// - different default
   846  	mr.WithDefaultBaseUrl("foo.com/some/path/")
   847  	a, err = mr.MarshalAny(md.AsProto())
   848  	testutil.Ok(t, err)
   849  	testutil.Eq(t, "foo.com/some/path/google.protobuf.DescriptorProto", a.TypeUrl)
   850  
   851  	// - custom base URL for package
   852  	mr.AddBaseUrlForElement("bar.com/other/", "google.protobuf")
   853  	a, err = mr.MarshalAny(md.AsProto())
   854  	testutil.Ok(t, err)
   855  	testutil.Eq(t, "bar.com/other/google.protobuf.DescriptorProto", a.TypeUrl)
   856  
   857  	// - custom base URL for type
   858  	mr.AddBaseUrlForElement("http://baz.com/another/", "google.protobuf.DescriptorProto")
   859  	a, err = mr.MarshalAny(md.AsProto())
   860  	testutil.Ok(t, err)
   861  	testutil.Eq(t, "http://baz.com/another/google.protobuf.DescriptorProto", a.TypeUrl)
   862  }
   863  
   864  func TestMessageRegistry_MessageDescriptorToPType(t *testing.T) {
   865  	protoSource := `
   866  		syntax = "proto2";
   867  		package foo;
   868  		message Bar {
   869  			optional string abc = 1 [deprecated = true];
   870  			repeated int32 def = 2 [packed = true];
   871  			optional string ghi = 3 [default = "foobar"];
   872  			oneof oo {
   873  				uint64 nid = 4;
   874  				string sid = 5;
   875  			}
   876  		}`
   877  	p := protoparse.Parser{
   878  		Accessor: func(filename string) (io.ReadCloser, error) {
   879  			if filename == "test.proto" {
   880  				return ioutil.NopCloser(strings.NewReader(protoSource)), nil
   881  			}
   882  			return nil, os.ErrNotExist
   883  		},
   884  	}
   885  	fds, err := p.ParseFiles("test.proto")
   886  	testutil.Ok(t, err)
   887  	fd := fds[0]
   888  
   889  	msg := NewMessageRegistryWithDefaults().MessageAsPType(fd.GetMessageTypes()[0])
   890  
   891  	// quick check of the resulting message's properties
   892  	testutil.Eq(t, "foo.Bar", msg.Name)
   893  	testutil.Eq(t, []string{"oo"}, msg.Oneofs)
   894  	testutil.Eq(t, ptype.Syntax_SYNTAX_PROTO2, msg.Syntax)
   895  	testutil.Eq(t, "test.proto", msg.SourceContext.GetFileName())
   896  	testutil.Eq(t, 0, len(msg.Options))
   897  	testutil.Eq(t, 5, len(msg.Fields))
   898  
   899  	testutil.Eq(t, "abc", msg.Fields[0].Name)
   900  	testutil.Eq(t, ptype.Field_CARDINALITY_OPTIONAL, msg.Fields[0].Cardinality)
   901  	testutil.Eq(t, ptype.Field_TYPE_STRING, msg.Fields[0].Kind)
   902  	testutil.Eq(t, "", msg.Fields[0].DefaultValue)
   903  	testutil.Eq(t, int32(1), msg.Fields[0].Number)
   904  	testutil.Eq(t, int32(0), msg.Fields[0].OneofIndex)
   905  	testutil.Eq(t, 1, len(msg.Fields[0].Options))
   906  	testutil.Eq(t, "deprecated", msg.Fields[0].Options[0].Name)
   907  	// make sure the value is a wrapped bool
   908  	var v ptypes.DynamicAny
   909  	err = ptypes.UnmarshalAny(msg.Fields[0].Options[0].Value, &v)
   910  	testutil.Ok(t, err)
   911  	testutil.Ceq(t, &wrappers.BoolValue{Value: true}, v.Message, eqpm)
   912  
   913  	testutil.Eq(t, "def", msg.Fields[1].Name)
   914  	testutil.Eq(t, ptype.Field_CARDINALITY_REPEATED, msg.Fields[1].Cardinality)
   915  	testutil.Eq(t, ptype.Field_TYPE_INT32, msg.Fields[1].Kind)
   916  	testutil.Eq(t, "", msg.Fields[1].DefaultValue)
   917  	testutil.Eq(t, int32(2), msg.Fields[1].Number)
   918  	testutil.Eq(t, int32(0), msg.Fields[1].OneofIndex)
   919  	testutil.Eq(t, true, msg.Fields[1].Packed)
   920  	testutil.Eq(t, 0, len(msg.Fields[1].Options))
   921  
   922  	testutil.Eq(t, "ghi", msg.Fields[2].Name)
   923  	testutil.Eq(t, ptype.Field_CARDINALITY_OPTIONAL, msg.Fields[2].Cardinality)
   924  	testutil.Eq(t, ptype.Field_TYPE_STRING, msg.Fields[2].Kind)
   925  	testutil.Eq(t, "foobar", msg.Fields[2].DefaultValue)
   926  	testutil.Eq(t, int32(3), msg.Fields[2].Number)
   927  	testutil.Eq(t, int32(0), msg.Fields[2].OneofIndex)
   928  	testutil.Eq(t, 0, len(msg.Fields[2].Options))
   929  
   930  	testutil.Eq(t, "nid", msg.Fields[3].Name)
   931  	testutil.Eq(t, ptype.Field_CARDINALITY_OPTIONAL, msg.Fields[3].Cardinality)
   932  	testutil.Eq(t, ptype.Field_TYPE_UINT64, msg.Fields[3].Kind)
   933  	testutil.Eq(t, "", msg.Fields[3].DefaultValue)
   934  	testutil.Eq(t, int32(4), msg.Fields[3].Number)
   935  	testutil.Eq(t, int32(1), msg.Fields[3].OneofIndex)
   936  	testutil.Eq(t, 0, len(msg.Fields[3].Options))
   937  
   938  	testutil.Eq(t, "sid", msg.Fields[4].Name)
   939  	testutil.Eq(t, ptype.Field_CARDINALITY_OPTIONAL, msg.Fields[4].Cardinality)
   940  	testutil.Eq(t, ptype.Field_TYPE_STRING, msg.Fields[4].Kind)
   941  	testutil.Eq(t, "", msg.Fields[4].DefaultValue)
   942  	testutil.Eq(t, int32(5), msg.Fields[4].Number)
   943  	testutil.Eq(t, int32(1), msg.Fields[4].OneofIndex)
   944  	testutil.Eq(t, 0, len(msg.Fields[4].Options))
   945  }
   946  
   947  func TestMessageRegistry_EnumDescriptorToPType(t *testing.T) {
   948  	protoSource := `
   949  		syntax = "proto2";
   950  		package foo;
   951  		enum Bar {
   952  			option allow_alias = true;
   953  			ZERO = 0;
   954  			__UNSET__ = 0 [deprecated = true];
   955  			ONE = 1;
   956  			TWO = 2;
   957  			THREE = 3;
   958  		}`
   959  	p := protoparse.Parser{
   960  		Accessor: func(filename string) (io.ReadCloser, error) {
   961  			if filename == "test.proto" {
   962  				return ioutil.NopCloser(strings.NewReader(protoSource)), nil
   963  			}
   964  			return nil, os.ErrNotExist
   965  		},
   966  	}
   967  	fds, err := p.ParseFiles("test.proto")
   968  	testutil.Ok(t, err)
   969  	fd := fds[0]
   970  
   971  	enum := NewMessageRegistryWithDefaults().EnumAsPType(fd.GetEnumTypes()[0])
   972  
   973  	// quick check of the resulting message's properties
   974  	testutil.Eq(t, "foo.Bar", enum.Name)
   975  	testutil.Eq(t, ptype.Syntax_SYNTAX_PROTO2, enum.Syntax)
   976  	testutil.Eq(t, "test.proto", enum.SourceContext.GetFileName())
   977  	testutil.Eq(t, 5, len(enum.Enumvalue))
   978  	testutil.Eq(t, 1, len(enum.Options))
   979  	testutil.Eq(t, "allow_alias", enum.Options[0].Name)
   980  	// make sure the value is a wrapped bool
   981  	var v ptypes.DynamicAny
   982  	err = ptypes.UnmarshalAny(enum.Options[0].Value, &v)
   983  	testutil.Ok(t, err)
   984  	testutil.Ceq(t, &wrappers.BoolValue{Value: true}, v.Message, eqpm)
   985  
   986  	testutil.Eq(t, "ZERO", enum.Enumvalue[0].Name)
   987  	testutil.Eq(t, int32(0), enum.Enumvalue[0].Number)
   988  	testutil.Eq(t, 0, len(enum.Enumvalue[0].Options))
   989  
   990  	testutil.Eq(t, "__UNSET__", enum.Enumvalue[1].Name)
   991  	testutil.Eq(t, int32(0), enum.Enumvalue[1].Number)
   992  	testutil.Eq(t, 1, len(enum.Enumvalue[1].Options))
   993  	testutil.Eq(t, "deprecated", enum.Enumvalue[1].Options[0].Name)
   994  	// make sure the value is a wrapped bool
   995  	err = ptypes.UnmarshalAny(enum.Enumvalue[1].Options[0].Value, &v)
   996  	testutil.Ok(t, err)
   997  	testutil.Ceq(t, &wrappers.BoolValue{Value: true}, v.Message, eqpm)
   998  
   999  	testutil.Eq(t, "ONE", enum.Enumvalue[2].Name)
  1000  	testutil.Eq(t, int32(1), enum.Enumvalue[2].Number)
  1001  	testutil.Eq(t, 0, len(enum.Enumvalue[2].Options))
  1002  
  1003  	testutil.Eq(t, "TWO", enum.Enumvalue[3].Name)
  1004  	testutil.Eq(t, int32(2), enum.Enumvalue[3].Number)
  1005  	testutil.Eq(t, 0, len(enum.Enumvalue[3].Options))
  1006  
  1007  	testutil.Eq(t, "THREE", enum.Enumvalue[4].Name)
  1008  	testutil.Eq(t, int32(3), enum.Enumvalue[4].Number)
  1009  	testutil.Eq(t, 0, len(enum.Enumvalue[4].Options))
  1010  }
  1011  
  1012  func TestMessageRegistry_ServiceDescriptorToApi(t *testing.T) {
  1013  	// TODO
  1014  }
  1015  
  1016  func eqm(a, b interface{}) bool {
  1017  	return dynamic.MessagesEqual(a.(proto.Message), b.(proto.Message))
  1018  }
  1019  
  1020  func eqpm(a, b interface{}) bool {
  1021  	return proto.Equal(a.(proto.Message), b.(proto.Message))
  1022  }