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