github.com/syumai/protoreflect@v1.7.1-0.20200810020253-2ac7e3b3a321/desc/builder/builder_test.go (about)

     1  package builder
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"os"
     7  	"sort"
     8  	"strings"
     9  	"testing"
    10  
    11  	"github.com/golang/protobuf/proto"
    12  	dpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
    13  	"github.com/golang/protobuf/ptypes/any"
    14  	"github.com/golang/protobuf/ptypes/empty"
    15  	"github.com/golang/protobuf/ptypes/timestamp"
    16  
    17  	"github.com/syumai/protoreflect/desc"
    18  	"github.com/syumai/protoreflect/dynamic"
    19  	_ "github.com/syumai/protoreflect/internal/testprotos"
    20  	"github.com/syumai/protoreflect/internal/testutil"
    21  )
    22  
    23  func TestSimpleDescriptorsFromScratch(t *testing.T) {
    24  	md, err := desc.LoadMessageDescriptorForMessage((*empty.Empty)(nil))
    25  	testutil.Ok(t, err)
    26  
    27  	file := NewFile("foo/bar.proto").SetPackageName("foo.bar")
    28  	en := NewEnum("Options").
    29  		AddValue(NewEnumValue("OPTION_1")).
    30  		AddValue(NewEnumValue("OPTION_2")).
    31  		AddValue(NewEnumValue("OPTION_3"))
    32  	file.AddEnum(en)
    33  
    34  	msg := NewMessage("FooRequest").
    35  		AddField(NewField("id", FieldTypeInt64())).
    36  		AddField(NewField("name", FieldTypeString())).
    37  		AddField(NewField("options", FieldTypeEnum(en)).
    38  			SetRepeated())
    39  	file.AddMessage(msg)
    40  
    41  	sb := NewService("FooService").
    42  		AddMethod(NewMethod("DoSomething", RpcTypeMessage(msg, false), RpcTypeMessage(msg, false))).
    43  		AddMethod(NewMethod("ReturnThings", RpcTypeImportedMessage(md, false), RpcTypeMessage(msg, true)))
    44  	file.AddService(sb)
    45  
    46  	fd, err := file.Build()
    47  	testutil.Ok(t, err)
    48  
    49  	testutil.Eq(t, []*desc.FileDescriptor{md.GetFile()}, fd.GetDependencies())
    50  	testutil.Require(t, fd.FindEnum("foo.bar.Options") != nil)
    51  	testutil.Eq(t, 3, len(fd.FindEnum("foo.bar.Options").GetValues()))
    52  	testutil.Require(t, fd.FindMessage("foo.bar.FooRequest") != nil)
    53  	testutil.Eq(t, 3, len(fd.FindMessage("foo.bar.FooRequest").GetFields()))
    54  	testutil.Require(t, fd.FindService("foo.bar.FooService") != nil)
    55  	testutil.Eq(t, 2, len(fd.FindService("foo.bar.FooService").GetMethods()))
    56  
    57  	// building the others produces same results
    58  	ed, err := en.Build()
    59  	testutil.Ok(t, err)
    60  	testutil.Require(t, proto.Equal(ed.AsProto(), fd.FindEnum("foo.bar.Options").AsProto()))
    61  
    62  	md, err = msg.Build()
    63  	testutil.Ok(t, err)
    64  	testutil.Require(t, proto.Equal(md.AsProto(), fd.FindMessage("foo.bar.FooRequest").AsProto()))
    65  
    66  	sd, err := sb.Build()
    67  	testutil.Ok(t, err)
    68  	testutil.Require(t, proto.Equal(sd.AsProto(), fd.FindService("foo.bar.FooService").AsProto()))
    69  }
    70  
    71  func TestSimpleDescriptorsFromScratch_SyntheticFiles(t *testing.T) {
    72  	md, err := desc.LoadMessageDescriptorForMessage((*empty.Empty)(nil))
    73  	testutil.Ok(t, err)
    74  
    75  	en := NewEnum("Options")
    76  	en.AddValue(NewEnumValue("OPTION_1"))
    77  	en.AddValue(NewEnumValue("OPTION_2"))
    78  	en.AddValue(NewEnumValue("OPTION_3"))
    79  
    80  	msg := NewMessage("FooRequest")
    81  	msg.AddField(NewField("id", FieldTypeInt64()))
    82  	msg.AddField(NewField("name", FieldTypeString()))
    83  	msg.AddField(NewField("options", FieldTypeEnum(en)).
    84  		SetRepeated())
    85  
    86  	sb := NewService("FooService")
    87  	sb.AddMethod(NewMethod("DoSomething", RpcTypeMessage(msg, false), RpcTypeMessage(msg, false)))
    88  	sb.AddMethod(NewMethod("ReturnThings", RpcTypeImportedMessage(md, false), RpcTypeMessage(msg, true)))
    89  
    90  	sd, err := sb.Build()
    91  	testutil.Ok(t, err)
    92  	testutil.Eq(t, "FooService", sd.GetFullyQualifiedName())
    93  	testutil.Eq(t, 2, len(sd.GetMethods()))
    94  
    95  	// it imports google/protobuf/empty.proto and a synthetic file that has message
    96  	testutil.Eq(t, 2, len(sd.GetFile().GetDependencies()))
    97  	fd := sd.GetFile().GetDependencies()[0]
    98  	testutil.Eq(t, "google/protobuf/empty.proto", fd.GetName())
    99  	testutil.Eq(t, md.GetFile(), fd)
   100  	fd = sd.GetFile().GetDependencies()[1]
   101  	testutil.Require(t, strings.Contains(fd.GetName(), "generated"))
   102  	testutil.Require(t, fd.FindMessage("FooRequest") != nil)
   103  	testutil.Eq(t, 3, len(fd.FindMessage("FooRequest").GetFields()))
   104  
   105  	// this one imports only a synthetic file that has enum
   106  	testutil.Eq(t, 1, len(fd.GetDependencies()))
   107  	fd2 := fd.GetDependencies()[0]
   108  	testutil.Require(t, fd2.FindEnum("Options") != nil)
   109  	testutil.Eq(t, 3, len(fd2.FindEnum("Options").GetValues()))
   110  
   111  	// building the others produces same results
   112  	ed, err := en.Build()
   113  	testutil.Ok(t, err)
   114  	testutil.Require(t, proto.Equal(ed.AsProto(), fd2.FindEnum("Options").AsProto()))
   115  
   116  	md, err = msg.Build()
   117  	testutil.Ok(t, err)
   118  	testutil.Require(t, proto.Equal(md.AsProto(), fd.FindMessage("FooRequest").AsProto()))
   119  }
   120  
   121  func TestComplexDescriptorsFromScratch(t *testing.T) {
   122  	mdEmpty, err := desc.LoadMessageDescriptorForMessage((*empty.Empty)(nil))
   123  	testutil.Ok(t, err)
   124  	mdAny, err := desc.LoadMessageDescriptorForMessage((*any.Any)(nil))
   125  	testutil.Ok(t, err)
   126  	mdTimestamp, err := desc.LoadMessageDescriptorForMessage((*timestamp.Timestamp)(nil))
   127  	testutil.Ok(t, err)
   128  
   129  	msgA := NewMessage("FooA").
   130  		AddField(NewField("id", FieldTypeUInt64())).
   131  		AddField(NewField("when", FieldTypeImportedMessage(mdTimestamp))).
   132  		AddField(NewField("extras", FieldTypeImportedMessage(mdAny)).
   133  			SetRepeated()).
   134  		SetExtensionRanges([]*dpb.DescriptorProto_ExtensionRange{{Start: proto.Int32(100), End: proto.Int32(201)}})
   135  	msgA2 := NewMessage("Nnn").
   136  		AddField(NewField("uid1", FieldTypeFixed64())).
   137  		AddField(NewField("uid2", FieldTypeFixed64()))
   138  	NewFile("").
   139  		SetPackageName("foo.bar").
   140  		AddMessage(msgA).
   141  		AddMessage(msgA2)
   142  
   143  	msgB := NewMessage("FooB").
   144  		AddField(NewField("foo_a", FieldTypeMessage(msgA)).
   145  			SetRepeated()).
   146  		AddField(NewField("name", FieldTypeString()))
   147  	NewFile("").
   148  		SetPackageName("foo.bar").
   149  		AddMessage(msgB)
   150  
   151  	enC := NewEnum("Vals").
   152  		AddValue(NewEnumValue("DEFAULT")).
   153  		AddValue(NewEnumValue("VALUE_A")).
   154  		AddValue(NewEnumValue("VALUE_B")).
   155  		AddValue(NewEnumValue("VALUE_C"))
   156  	msgC := NewMessage("BarBaz").
   157  		AddOneOf(NewOneOf("bbb").
   158  			AddChoice(NewField("b1", FieldTypeMessage(msgA))).
   159  			AddChoice(NewField("b2", FieldTypeMessage(msgB)))).
   160  		AddField(NewField("v", FieldTypeEnum(enC)))
   161  	NewFile("some/path/file.proto").
   162  		SetPackageName("foo.baz").
   163  		AddEnum(enC).
   164  		AddMessage(msgC)
   165  
   166  	enD := NewEnum("Ppp").
   167  		AddValue(NewEnumValue("P0")).
   168  		AddValue(NewEnumValue("P1")).
   169  		AddValue(NewEnumValue("P2")).
   170  		AddValue(NewEnumValue("P3"))
   171  	exD := NewExtension("ppp", 123, FieldTypeEnum(enD), msgA)
   172  	NewFile("some/other/path/file.proto").
   173  		SetPackageName("foo.biz").
   174  		AddEnum(enD).
   175  		AddExtension(exD)
   176  
   177  	msgE := NewMessage("Ppp").
   178  		AddField(NewField("p", FieldTypeEnum(enD))).
   179  		AddField(NewField("n", FieldTypeMessage(msgA2)))
   180  	fd, err := NewFile("").
   181  		SetPackageName("foo.bar").
   182  		AddMessage(msgE).
   183  		AddService(NewService("PppSvc").
   184  			AddMethod(NewMethod("Method1", RpcTypeMessage(msgE, false), RpcTypeImportedMessage(mdEmpty, false))).
   185  			AddMethod(NewMethod("Method2", RpcTypeMessage(msgB, false), RpcTypeMessage(msgC, true)))).
   186  		Build()
   187  
   188  	testutil.Ok(t, err)
   189  
   190  	testutil.Eq(t, 5, len(fd.GetDependencies()))
   191  	// dependencies sorted; those with generated names come last
   192  	depEmpty := fd.GetDependencies()[0]
   193  	testutil.Eq(t, "google/protobuf/empty.proto", depEmpty.GetName())
   194  	testutil.Eq(t, mdEmpty.GetFile(), depEmpty)
   195  	depD := fd.GetDependencies()[1]
   196  	testutil.Eq(t, "some/other/path/file.proto", depD.GetName())
   197  	depC := fd.GetDependencies()[2]
   198  	testutil.Eq(t, "some/path/file.proto", depC.GetName())
   199  	depA := fd.GetDependencies()[3]
   200  	testutil.Require(t, strings.Contains(depA.GetName(), "generated"))
   201  	depB := fd.GetDependencies()[4]
   202  	testutil.Require(t, strings.Contains(depB.GetName(), "generated"))
   203  
   204  	// check contents of files
   205  	testutil.Require(t, depA.FindMessage("foo.bar.FooA") != nil)
   206  	testutil.Eq(t, 3, len(depA.FindMessage("foo.bar.FooA").GetFields()))
   207  	testutil.Require(t, depA.FindMessage("foo.bar.Nnn") != nil)
   208  	testutil.Eq(t, 2, len(depA.FindMessage("foo.bar.Nnn").GetFields()))
   209  
   210  	testutil.Require(t, depB.FindMessage("foo.bar.FooB") != nil)
   211  	testutil.Eq(t, 2, len(depB.FindMessage("foo.bar.FooB").GetFields()))
   212  
   213  	testutil.Require(t, depC.FindMessage("foo.baz.BarBaz") != nil)
   214  	testutil.Eq(t, 3, len(depC.FindMessage("foo.baz.BarBaz").GetFields()))
   215  	testutil.Require(t, depC.FindEnum("foo.baz.Vals") != nil)
   216  	testutil.Eq(t, 4, len(depC.FindEnum("foo.baz.Vals").GetValues()))
   217  
   218  	testutil.Require(t, depD.FindEnum("foo.biz.Ppp") != nil)
   219  	testutil.Eq(t, 4, len(depD.FindEnum("foo.biz.Ppp").GetValues()))
   220  	testutil.Require(t, depD.FindExtensionByName("foo.biz.ppp") != nil)
   221  
   222  	testutil.Require(t, fd.FindMessage("foo.bar.Ppp") != nil)
   223  	testutil.Eq(t, 2, len(fd.FindMessage("foo.bar.Ppp").GetFields()))
   224  	testutil.Require(t, fd.FindService("foo.bar.PppSvc") != nil)
   225  	testutil.Eq(t, 2, len(fd.FindService("foo.bar.PppSvc").GetMethods()))
   226  }
   227  
   228  func TestCreatingGroupField(t *testing.T) {
   229  	grpMb := NewMessage("GroupA").
   230  		AddField(NewField("name", FieldTypeString())).
   231  		AddField(NewField("id", FieldTypeInt64()))
   232  	grpFlb := NewGroupField(grpMb)
   233  
   234  	mb := NewMessage("TestMessage").
   235  		AddField(NewField("foo", FieldTypeBool())).
   236  		AddField(grpFlb)
   237  	md, err := mb.Build()
   238  	testutil.Ok(t, err)
   239  
   240  	testutil.Require(t, md.FindFieldByName("groupa") != nil)
   241  	testutil.Eq(t, dpb.FieldDescriptorProto_TYPE_GROUP, md.FindFieldByName("groupa").GetType())
   242  	nmd := md.GetNestedMessageTypes()[0]
   243  	testutil.Eq(t, "GroupA", nmd.GetName())
   244  	testutil.Eq(t, nmd, md.FindFieldByName("groupa").GetMessageType())
   245  
   246  	// try a rename that will fail
   247  	err = grpMb.TrySetName("fooBarBaz")
   248  	testutil.Require(t, err != nil)
   249  	testutil.Eq(t, "group name fooBarBaz must start with capital letter", err.Error())
   250  	// failed rename should not have modified any state
   251  	md2, err := mb.Build()
   252  	testutil.Ok(t, err)
   253  	testutil.Require(t, proto.Equal(md.AsProto(), md2.AsProto()))
   254  	// another attempt that will fail
   255  	err = grpFlb.TrySetName("foobarbaz")
   256  	testutil.Require(t, err != nil)
   257  	testutil.Eq(t, "cannot change name of group field TestMessage.groupa; change name of group instead", err.Error())
   258  	// again, no state should have been modified
   259  	md2, err = mb.Build()
   260  	testutil.Ok(t, err)
   261  	testutil.Require(t, proto.Equal(md.AsProto(), md2.AsProto()))
   262  
   263  	// and a rename that succeeds
   264  	err = grpMb.TrySetName("FooBarBaz")
   265  	testutil.Ok(t, err)
   266  	md, err = mb.Build()
   267  	testutil.Ok(t, err)
   268  
   269  	// field also renamed
   270  	testutil.Require(t, md.FindFieldByName("foobarbaz") != nil)
   271  	testutil.Eq(t, dpb.FieldDescriptorProto_TYPE_GROUP, md.FindFieldByName("foobarbaz").GetType())
   272  	nmd = md.GetNestedMessageTypes()[0]
   273  	testutil.Eq(t, "FooBarBaz", nmd.GetName())
   274  	testutil.Eq(t, nmd, md.FindFieldByName("foobarbaz").GetMessageType())
   275  }
   276  
   277  func TestCreatingMapField(t *testing.T) {
   278  	mapFlb := NewMapField("countsByName", FieldTypeString(), FieldTypeUInt64())
   279  	testutil.Require(t, mapFlb.IsMap())
   280  
   281  	mb := NewMessage("TestMessage").
   282  		AddField(NewField("foo", FieldTypeBool())).
   283  		AddField(mapFlb)
   284  	md, err := mb.Build()
   285  	testutil.Ok(t, err)
   286  
   287  	testutil.Require(t, md.FindFieldByName("countsByName") != nil)
   288  	testutil.Require(t, md.FindFieldByName("countsByName").IsMap())
   289  	nmd := md.GetNestedMessageTypes()[0]
   290  	testutil.Eq(t, "CountsByNameEntry", nmd.GetName())
   291  	testutil.Eq(t, nmd, md.FindFieldByName("countsByName").GetMessageType())
   292  
   293  	// try a rename that will fail
   294  	err = mapFlb.GetType().localMsgType.TrySetName("fooBarBaz")
   295  	testutil.Require(t, err != nil)
   296  	testutil.Eq(t, "cannot change name of map entry TestMessage.CountsByNameEntry; change name of field instead", err.Error())
   297  	// failed rename should not have modified any state
   298  	md2, err := mb.Build()
   299  	testutil.Ok(t, err)
   300  	testutil.Require(t, proto.Equal(md.AsProto(), md2.AsProto()))
   301  
   302  	// and a rename that succeeds
   303  	err = mapFlb.TrySetName("fooBarBaz")
   304  	testutil.Ok(t, err)
   305  	md, err = mb.Build()
   306  	testutil.Ok(t, err)
   307  
   308  	// map entry also renamed
   309  	testutil.Require(t, md.FindFieldByName("fooBarBaz") != nil)
   310  	testutil.Require(t, md.FindFieldByName("fooBarBaz").IsMap())
   311  	nmd = md.GetNestedMessageTypes()[0]
   312  	testutil.Eq(t, "FooBarBazEntry", nmd.GetName())
   313  	testutil.Eq(t, nmd, md.FindFieldByName("fooBarBaz").GetMessageType())
   314  }
   315  
   316  func TestProto3Optional(t *testing.T) {
   317  	mb := NewMessage("Foo")
   318  	flb := NewField("bar", FieldTypeBool()).SetProto3Optional(true)
   319  	mb.AddField(flb)
   320  
   321  	_, err := flb.Build()
   322  	testutil.Nok(t, err) // file does not have proto3 syntax
   323  
   324  	fb := NewFile("foo.proto").SetProto3(true)
   325  	fb.AddMessage(mb)
   326  
   327  	fld, err := flb.Build()
   328  	testutil.Ok(t, err)
   329  
   330  	testutil.Require(t, fld.IsProto3Optional())
   331  	testutil.Require(t, fld.GetOneOf() != nil)
   332  	testutil.Require(t, fld.GetOneOf().IsSynthetic())
   333  	testutil.Eq(t, "_bar", fld.GetOneOf().GetName())
   334  }
   335  
   336  func TestBuildersFromDescriptors(t *testing.T) {
   337  	for _, s := range []string{"desc_test1.proto", "desc_test2.proto", "desc_test_defaults.proto", "desc_test_options.proto", "desc_test_proto3.proto", "desc_test_wellknowntypes.proto", "nopkg/desc_test_nopkg.proto", "nopkg/desc_test_nopkg_new.proto", "pkg/desc_test_pkg.proto"} {
   338  		fd, err := desc.LoadFileDescriptor(s)
   339  		testutil.Ok(t, err)
   340  		roundTripFile(t, fd)
   341  	}
   342  }
   343  
   344  func TestBuildersFromDescriptors_PreserveComments(t *testing.T) {
   345  	fd, err := loadProtoset("../../internal/testprotos/desc_test1.protoset")
   346  	testutil.Ok(t, err)
   347  
   348  	fb, err := FromFile(fd)
   349  	testutil.Ok(t, err)
   350  
   351  	count := 0
   352  	var checkBuilderComments func(b Builder)
   353  	checkBuilderComments = func(b Builder) {
   354  		hasComment := true
   355  		switch b := b.(type) {
   356  		case *FileBuilder:
   357  			hasComment = false
   358  		case *FieldBuilder:
   359  			// comments for groups are on the message, not the field
   360  			hasComment = b.GetType().GetType() != dpb.FieldDescriptorProto_TYPE_GROUP
   361  		case *MessageBuilder:
   362  			// comments for maps are on the field, not the entry message
   363  			if b.Options.GetMapEntry() {
   364  				// we just return to also skip checking child elements
   365  				// (map entry child elements are synthetic and have no comments)
   366  				return
   367  			}
   368  		}
   369  
   370  		if hasComment {
   371  			count++
   372  			testutil.Eq(t, fmt.Sprintf(" Comment for %s\n", b.GetName()), b.GetComments().LeadingComment,
   373  				"wrong comment for builder %s", GetFullyQualifiedName(b))
   374  		}
   375  		for _, ch := range b.GetChildren() {
   376  			checkBuilderComments(ch)
   377  		}
   378  	}
   379  
   380  	checkBuilderComments(fb)
   381  	// sanity check that we didn't accidentally short-circuit above and fail to check comments
   382  	testutil.Require(t, count > 30, "too few elements checked")
   383  
   384  	// now check that they also come out in the resulting descriptor
   385  	fd, err = fb.Build()
   386  	testutil.Ok(t, err)
   387  
   388  	descCount := 0
   389  	var checkDescriptorComments func(d desc.Descriptor)
   390  	checkDescriptorComments = func(d desc.Descriptor) {
   391  		switch d := d.(type) {
   392  		case *desc.FileDescriptor:
   393  			for _, ch := range d.GetMessageTypes() {
   394  				checkDescriptorComments(ch)
   395  			}
   396  			for _, ch := range d.GetEnumTypes() {
   397  				checkDescriptorComments(ch)
   398  			}
   399  			for _, ch := range d.GetExtensions() {
   400  				checkDescriptorComments(ch)
   401  			}
   402  			for _, ch := range d.GetServices() {
   403  				checkDescriptorComments(ch)
   404  			}
   405  			// files don't have comments, so bail out before check below
   406  			return
   407  		case *desc.MessageDescriptor:
   408  			if d.IsMapEntry() {
   409  				// map entry messages have no comments (and neither do their child fields)
   410  				return
   411  			}
   412  			for _, ch := range d.GetFields() {
   413  				checkDescriptorComments(ch)
   414  			}
   415  			for _, ch := range d.GetNestedMessageTypes() {
   416  				checkDescriptorComments(ch)
   417  			}
   418  			for _, ch := range d.GetNestedEnumTypes() {
   419  				checkDescriptorComments(ch)
   420  			}
   421  			for _, ch := range d.GetNestedExtensions() {
   422  				checkDescriptorComments(ch)
   423  			}
   424  			for _, ch := range d.GetOneOfs() {
   425  				checkDescriptorComments(ch)
   426  			}
   427  		case *desc.FieldDescriptor:
   428  			if d.GetType() == dpb.FieldDescriptorProto_TYPE_GROUP {
   429  				// groups comments are on the message, not hte field; so bail out before check below
   430  				return
   431  			}
   432  		case *desc.EnumDescriptor:
   433  			for _, ch := range d.GetValues() {
   434  				checkDescriptorComments(ch)
   435  			}
   436  		case *desc.ServiceDescriptor:
   437  			for _, ch := range d.GetMethods() {
   438  				checkDescriptorComments(ch)
   439  			}
   440  		}
   441  
   442  		descCount++
   443  		testutil.Eq(t, fmt.Sprintf(" Comment for %s\n", d.GetName()), d.GetSourceInfo().GetLeadingComments(),
   444  			"wrong comment for descriptor %s", d.GetFullyQualifiedName())
   445  	}
   446  
   447  	checkDescriptorComments(fd)
   448  	testutil.Eq(t, count, descCount)
   449  }
   450  
   451  func loadProtoset(path string) (*desc.FileDescriptor, error) {
   452  	var fds dpb.FileDescriptorSet
   453  	f, err := os.Open(path)
   454  	if err != nil {
   455  		return nil, err
   456  	}
   457  	defer f.Close()
   458  	bb, err := ioutil.ReadAll(f)
   459  	if err != nil {
   460  		return nil, err
   461  	}
   462  	if err = proto.Unmarshal(bb, &fds); err != nil {
   463  		return nil, err
   464  	}
   465  	return desc.CreateFileDescriptorFromSet(&fds)
   466  }
   467  
   468  func roundTripFile(t *testing.T, fd *desc.FileDescriptor) {
   469  	// First, recursively verify that every child element can be converted to a
   470  	// Builder and back without loss of fidelity.
   471  	for _, md := range fd.GetMessageTypes() {
   472  		roundTripMessage(t, md)
   473  	}
   474  	for _, ed := range fd.GetEnumTypes() {
   475  		roundTripEnum(t, ed)
   476  	}
   477  	for _, exd := range fd.GetExtensions() {
   478  		roundTripField(t, exd)
   479  	}
   480  	for _, sd := range fd.GetServices() {
   481  		roundTripService(t, sd)
   482  	}
   483  
   484  	// Finally, we check the whole file itself.
   485  	fb, err := FromFile(fd)
   486  	testutil.Ok(t, err)
   487  
   488  	roundTripped, err := fb.Build()
   489  	testutil.Ok(t, err)
   490  
   491  	// Round tripping from a file descriptor to a builder and back will
   492  	// experience some minor changes (that do not impact the semantics of
   493  	// any of the file's contents):
   494  	//  1. The builder sorts dependencies. However the original file
   495  	//     descriptor has dependencies in the order they appear in import
   496  	//     statements in the source file.
   497  	//  2. The builder imports the actual source of all elements and never
   498  	//     uses public imports. The original file, on the other hand, could
   499  	//     used public imports and "indirectly" import other files that way.
   500  	//  3. The builder never emits weak imports.
   501  	//  4. The builder tries to preserve SourceCodeInfo, but will not preserve
   502  	//     position information. So that info does not survive round-tripping
   503  	//     (though comments do: there is a separate test for that). Also, the
   504  	//     round-tripped version will have source code info (even though it
   505  	//     may have no comments and zero position info), even if the original
   506  	//     descriptor had none.
   507  	// So we're going to modify the original descriptor in the same ways.
   508  	// That way, a simple proto.Equal() check will suffice to confirm that
   509  	// the file descriptor survived the round trip.
   510  
   511  	// The files we are testing have one occurrence of a public import. The
   512  	// file nopkg/desc_test_nopkg.proto declares nothing and public imports
   513  	// nopkg/desc_test_nopkg_new.proto. So any file that depends on the
   514  	// former will be updated to instead depend on the latter (since it is
   515  	// the actual file that declares used elements).
   516  	fdp := fd.AsFileDescriptorProto()
   517  	needsNopkgNew := false
   518  	hasNoPkgNew := false
   519  	for _, dep := range fdp.Dependency {
   520  		if dep == "nopkg/desc_test_nopkg.proto" {
   521  			needsNopkgNew = true
   522  		}
   523  		if dep == "nopkg/desc_test_nopkg_new.proto" {
   524  			hasNoPkgNew = false
   525  		}
   526  	}
   527  	if needsNopkgNew && !hasNoPkgNew {
   528  		fdp.Dependency = append(fdp.Dependency, "nopkg/desc_test_nopkg_new.proto")
   529  	}
   530  
   531  	// Strip any public and weak imports. (The step above should have "fixed"
   532  	// files to handle any actual public import encountered.)
   533  	fdp.PublicDependency = nil
   534  	fdp.WeakDependency = nil
   535  
   536  	// Remove source code info that the builder generated since the original
   537  	// has none.
   538  	roundTripped.AsFileDescriptorProto().SourceCodeInfo = nil
   539  
   540  	// Finally, sort the imports. That way they match the built result (which
   541  	// is always sorted).
   542  	sort.Strings(fdp.Dependency)
   543  
   544  	// Now (after tweaking) the original should match the round-tripped descriptor:
   545  	testutil.Require(t, proto.Equal(fdp, roundTripped.AsProto()), "File %q failed round trip.\nExpecting: %s\nGot: %s\n",
   546  		fd.GetName(), proto.MarshalTextString(fdp), proto.MarshalTextString(roundTripped.AsProto()))
   547  }
   548  
   549  func roundTripMessage(t *testing.T, md *desc.MessageDescriptor) {
   550  	// first recursively validate all nested elements
   551  	for _, fld := range md.GetFields() {
   552  		roundTripField(t, fld)
   553  	}
   554  	for _, ood := range md.GetOneOfs() {
   555  		oob, err := FromOneOf(ood)
   556  		testutil.Ok(t, err)
   557  		roundTripped, err := oob.Build()
   558  		testutil.Ok(t, err)
   559  		checkDescriptors(t, ood, roundTripped)
   560  	}
   561  	for _, nmd := range md.GetNestedMessageTypes() {
   562  		roundTripMessage(t, nmd)
   563  	}
   564  	for _, ed := range md.GetNestedEnumTypes() {
   565  		roundTripEnum(t, ed)
   566  	}
   567  	for _, exd := range md.GetNestedExtensions() {
   568  		roundTripField(t, exd)
   569  	}
   570  
   571  	mb, err := FromMessage(md)
   572  	testutil.Ok(t, err)
   573  	roundTripped, err := mb.Build()
   574  	testutil.Ok(t, err)
   575  	checkDescriptors(t, md, roundTripped)
   576  }
   577  
   578  func roundTripEnum(t *testing.T, ed *desc.EnumDescriptor) {
   579  	// first recursively validate all nested elements
   580  	for _, evd := range ed.GetValues() {
   581  		evb, err := FromEnumValue(evd)
   582  		testutil.Ok(t, err)
   583  		roundTripped, err := evb.Build()
   584  		testutil.Ok(t, err)
   585  		checkDescriptors(t, evd, roundTripped)
   586  	}
   587  
   588  	eb, err := FromEnum(ed)
   589  	testutil.Ok(t, err)
   590  	roundTripped, err := eb.Build()
   591  	testutil.Ok(t, err)
   592  	checkDescriptors(t, ed, roundTripped)
   593  }
   594  
   595  func roundTripField(t *testing.T, fld *desc.FieldDescriptor) {
   596  	flb, err := FromField(fld)
   597  	testutil.Ok(t, err)
   598  	roundTripped, err := flb.Build()
   599  	testutil.Ok(t, err)
   600  	checkDescriptors(t, fld, roundTripped)
   601  }
   602  
   603  func roundTripService(t *testing.T, sd *desc.ServiceDescriptor) {
   604  	// first recursively validate all nested elements
   605  	for _, mtd := range sd.GetMethods() {
   606  		mtb, err := FromMethod(mtd)
   607  		testutil.Ok(t, err)
   608  		roundTripped, err := mtb.Build()
   609  		testutil.Ok(t, err)
   610  		checkDescriptors(t, mtd, roundTripped)
   611  	}
   612  
   613  	sb, err := FromService(sd)
   614  	testutil.Ok(t, err)
   615  	roundTripped, err := sb.Build()
   616  	testutil.Ok(t, err)
   617  	checkDescriptors(t, sd, roundTripped)
   618  }
   619  
   620  func checkDescriptors(t *testing.T, d1, d2 desc.Descriptor) {
   621  	testutil.Eq(t, d1.GetFullyQualifiedName(), d2.GetFullyQualifiedName())
   622  	testutil.Require(t, proto.Equal(d1.AsProto(), d2.AsProto()), "%s failed round trip.\nExpecting: %s\nGot: %s\n",
   623  		d1.GetFullyQualifiedName(), proto.MarshalTextString(d1.AsProto()), proto.MarshalTextString(d2.AsProto()))
   624  }
   625  
   626  func TestAddRemoveMoveBuilders(t *testing.T) {
   627  	// add field to one-of
   628  	fld1 := NewField("foo", FieldTypeInt32())
   629  	oo1 := NewOneOf("oofoo")
   630  	oo1.AddChoice(fld1)
   631  	checkChildren(t, oo1, fld1)
   632  	testutil.Eq(t, oo1.GetChoice("foo"), fld1)
   633  
   634  	// add one-of w/ field to a message
   635  	msg1 := NewMessage("foo")
   636  	msg1.AddOneOf(oo1)
   637  	checkChildren(t, msg1, oo1)
   638  	testutil.Eq(t, msg1.GetOneOf("oofoo"), oo1)
   639  	// field remains unchanged
   640  	testutil.Eq(t, fld1.GetParent(), oo1)
   641  	testutil.Eq(t, oo1.GetChoice("foo"), fld1)
   642  	// field also now registered with msg1
   643  	testutil.Eq(t, msg1.GetField("foo"), fld1)
   644  
   645  	// add empty one-of to message
   646  	oo2 := NewOneOf("oobar")
   647  	msg1.AddOneOf(oo2)
   648  	checkChildren(t, msg1, oo1, oo2)
   649  	testutil.Eq(t, msg1.GetOneOf("oobar"), oo2)
   650  	// now add field to that one-of
   651  	fld2 := NewField("bar", FieldTypeInt32())
   652  	oo2.AddChoice(fld2)
   653  	checkChildren(t, oo2, fld2)
   654  	testutil.Eq(t, oo2.GetChoice("bar"), fld2)
   655  	// field also now registered with msg1
   656  	testutil.Eq(t, msg1.GetField("bar"), fld2)
   657  
   658  	// add fails due to name collisions
   659  	fld1dup := NewField("foo", FieldTypeInt32())
   660  	err := oo1.TryAddChoice(fld1dup)
   661  	checkFailedAdd(t, err, oo1, fld1dup, "already contains field")
   662  	fld2 = NewField("bar", FieldTypeInt32())
   663  	err = msg1.TryAddField(fld2)
   664  	checkFailedAdd(t, err, msg1, fld2, "already contains element")
   665  	msg2 := NewMessage("oofoo")
   666  	// name collision can be different type
   667  	// (here, nested message conflicts with a one-of)
   668  	err = msg1.TryAddNestedMessage(msg2)
   669  	checkFailedAdd(t, err, msg1, msg2, "already contains element")
   670  
   671  	msg2 = NewMessage("baz")
   672  	msg1.AddNestedMessage(msg2)
   673  	checkChildren(t, msg1, oo1, oo2, msg2)
   674  	testutil.Eq(t, msg1.GetNestedMessage("baz"), msg2)
   675  
   676  	// can't add extension or map fields to one-of
   677  	ext1 := NewExtension("abc", 123, FieldTypeInt32(), msg1)
   678  	err = oo1.TryAddChoice(ext1)
   679  	checkFailedAdd(t, err, oo1, ext1, "is an extension, not a regular field")
   680  	err = msg1.TryAddField(ext1)
   681  	checkFailedAdd(t, err, msg1, ext1, "is an extension, not a regular field")
   682  	mapField := NewMapField("abc", FieldTypeInt32(), FieldTypeString())
   683  	err = oo1.TryAddChoice(mapField)
   684  	checkFailedAdd(t, err, oo1, mapField, "cannot add a map field")
   685  	// can add group field though
   686  	groupMsg := NewMessage("Group")
   687  	groupField := NewGroupField(groupMsg)
   688  	oo1.AddChoice(groupField)
   689  	checkChildren(t, oo1, fld1, groupField)
   690  	// adding map and group to msg succeeds
   691  	msg1.AddField(groupField)
   692  	msg1.AddField(mapField)
   693  	checkChildren(t, msg1, oo1, oo2, msg2, groupField, mapField)
   694  	// messages associated with map and group fields are not children of the
   695  	// message, but are in its scope and accessible via GetNestedMessage
   696  	testutil.Eq(t, msg1.GetNestedMessage("Group"), groupMsg)
   697  	testutil.Eq(t, msg1.GetNestedMessage("AbcEntry"), mapField.GetType().localMsgType)
   698  
   699  	// adding extension to message
   700  	ext2 := NewExtension("xyz", 234, FieldTypeInt32(), msg1)
   701  	msg1.AddNestedExtension(ext2)
   702  	checkChildren(t, msg1, oo1, oo2, msg2, groupField, mapField, ext2)
   703  	err = msg1.TryAddNestedExtension(ext1) // name collision
   704  	checkFailedAdd(t, err, msg1, ext1, "already contains element")
   705  	fld3 := NewField("ijk", FieldTypeString())
   706  	err = msg1.TryAddNestedExtension(fld3)
   707  	checkFailedAdd(t, err, msg1, fld3, "is not an extension")
   708  
   709  	// add enum values to enum
   710  	enumVal1 := NewEnumValue("A")
   711  	enum1 := NewEnum("bazel")
   712  	enum1.AddValue(enumVal1)
   713  	checkChildren(t, enum1, enumVal1)
   714  	testutil.Eq(t, enum1.GetValue("A"), enumVal1)
   715  	enumVal2 := NewEnumValue("B")
   716  	enum1.AddValue(enumVal2)
   717  	checkChildren(t, enum1, enumVal1, enumVal2)
   718  	testutil.Eq(t, enum1.GetValue("B"), enumVal2)
   719  	// fail w/ name collision
   720  	enumVal3 := NewEnumValue("B")
   721  	err = enum1.TryAddValue(enumVal3)
   722  	checkFailedAdd(t, err, enum1, enumVal3, "already contains value")
   723  
   724  	msg2.AddNestedEnum(enum1)
   725  	checkChildren(t, msg2, enum1)
   726  	testutil.Eq(t, msg2.GetNestedEnum("bazel"), enum1)
   727  	ext3 := NewExtension("bazel", 987, FieldTypeString(), msg2)
   728  	err = msg2.TryAddNestedExtension(ext3)
   729  	checkFailedAdd(t, err, msg2, ext3, "already contains element")
   730  
   731  	// services and methods
   732  	mtd1 := NewMethod("foo", RpcTypeMessage(msg1, false), RpcTypeMessage(msg1, false))
   733  	svc1 := NewService("FooService")
   734  	svc1.AddMethod(mtd1)
   735  	checkChildren(t, svc1, mtd1)
   736  	testutil.Eq(t, svc1.GetMethod("foo"), mtd1)
   737  	mtd2 := NewMethod("foo", RpcTypeMessage(msg1, false), RpcTypeMessage(msg1, false))
   738  	err = svc1.TryAddMethod(mtd2)
   739  	checkFailedAdd(t, err, svc1, mtd2, "already contains method")
   740  
   741  	// finally, test adding things to  a file
   742  	fb := NewFile("")
   743  	fb.AddMessage(msg1)
   744  	checkChildren(t, fb, msg1)
   745  	testutil.Eq(t, fb.GetMessage("foo"), msg1)
   746  	fb.AddService(svc1)
   747  	checkChildren(t, fb, msg1, svc1)
   748  	testutil.Eq(t, fb.GetService("FooService"), svc1)
   749  	enum2 := NewEnum("fizzle")
   750  	fb.AddEnum(enum2)
   751  	checkChildren(t, fb, msg1, svc1, enum2)
   752  	testutil.Eq(t, fb.GetEnum("fizzle"), enum2)
   753  	ext3 = NewExtension("foosball", 123, FieldTypeInt32(), msg1)
   754  	fb.AddExtension(ext3)
   755  	checkChildren(t, fb, msg1, svc1, enum2, ext3)
   756  	testutil.Eq(t, fb.GetExtension("foosball"), ext3)
   757  
   758  	// errors and name collisions
   759  	err = fb.TryAddExtension(fld3)
   760  	checkFailedAdd(t, err, fb, fld3, "is not an extension")
   761  	msg3 := NewMessage("fizzle")
   762  	err = fb.TryAddMessage(msg3)
   763  	checkFailedAdd(t, err, fb, msg3, "already contains element")
   764  	enum3 := NewEnum("foosball")
   765  	err = fb.TryAddEnum(enum3)
   766  	checkFailedAdd(t, err, fb, enum3, "already contains element")
   767  
   768  	// TODO: test moving and removing, too
   769  }
   770  
   771  func checkChildren(t *testing.T, parent Builder, children ...Builder) {
   772  	testutil.Eq(t, len(children), len(parent.GetChildren()), "Wrong number of children for %s (%T)", GetFullyQualifiedName(parent), parent)
   773  	ch := map[Builder]struct{}{}
   774  	for _, child := range children {
   775  		testutil.Eq(t, child.GetParent(), parent, "Child %s (%T) does not report %s (%T) as its parent", child.GetName(), child, GetFullyQualifiedName(parent), parent)
   776  		ch[child] = struct{}{}
   777  	}
   778  	for _, child := range parent.GetChildren() {
   779  		_, ok := ch[child]
   780  		testutil.Require(t, ok, "Child %s (%T) does appear in list of children for %s (%T)", child.GetName(), child, GetFullyQualifiedName(parent), parent)
   781  	}
   782  }
   783  
   784  func checkFailedAdd(t *testing.T, err error, parent Builder, child Builder, errorMsg string) {
   785  	testutil.Require(t, err != nil, "Expecting error assigning %s (%T) to %s (%T)", child.GetName(), child, GetFullyQualifiedName(parent), parent)
   786  	testutil.Require(t, strings.Contains(err.Error(), errorMsg), "Expecting error assigning %s (%T) to %s (%T) to contain text %q: %q", child.GetName(), child, GetFullyQualifiedName(parent), parent, errorMsg, err.Error())
   787  	testutil.Eq(t, nil, child.GetParent(), "Child %s (%T) should not have a parent after failed add", child.GetName(), child)
   788  	for _, ch := range parent.GetChildren() {
   789  		testutil.Require(t, ch != child, "Child %s (%T) should not appear in list of children for %s (%T) but does", child.GetName(), child, GetFullyQualifiedName(parent), parent)
   790  	}
   791  }
   792  
   793  func TestRenamingBuilders(t *testing.T) {
   794  	// TODO
   795  }
   796  
   797  func TestRenumberingFields(t *testing.T) {
   798  	// TODO
   799  }
   800  
   801  var (
   802  	fileOptionsDesc, msgOptionsDesc, fieldOptionsDesc, oneofOptionsDesc, extRangeOptionsDesc,
   803  	enumOptionsDesc, enumValOptionsDesc, svcOptionsDesc, mtdOptionsDesc *desc.MessageDescriptor
   804  )
   805  
   806  func init() {
   807  	var err error
   808  	fileOptionsDesc, err = desc.LoadMessageDescriptorForMessage((*dpb.FileOptions)(nil))
   809  	if err != nil {
   810  		panic(err)
   811  	}
   812  	msgOptionsDesc, err = desc.LoadMessageDescriptorForMessage((*dpb.MessageOptions)(nil))
   813  	if err != nil {
   814  		panic(err)
   815  	}
   816  	fieldOptionsDesc, err = desc.LoadMessageDescriptorForMessage((*dpb.FieldOptions)(nil))
   817  	if err != nil {
   818  		panic(err)
   819  	}
   820  	oneofOptionsDesc, err = desc.LoadMessageDescriptorForMessage((*dpb.OneofOptions)(nil))
   821  	if err != nil {
   822  		panic(err)
   823  	}
   824  	extRangeOptionsDesc, err = desc.LoadMessageDescriptorForMessage((*dpb.ExtensionRangeOptions)(nil))
   825  	if err != nil {
   826  		panic(err)
   827  	}
   828  	enumOptionsDesc, err = desc.LoadMessageDescriptorForMessage((*dpb.EnumOptions)(nil))
   829  	if err != nil {
   830  		panic(err)
   831  	}
   832  	enumValOptionsDesc, err = desc.LoadMessageDescriptorForMessage((*dpb.EnumValueOptions)(nil))
   833  	if err != nil {
   834  		panic(err)
   835  	}
   836  	svcOptionsDesc, err = desc.LoadMessageDescriptorForMessage((*dpb.ServiceOptions)(nil))
   837  	if err != nil {
   838  		panic(err)
   839  	}
   840  	mtdOptionsDesc, err = desc.LoadMessageDescriptorForMessage((*dpb.MethodOptions)(nil))
   841  	if err != nil {
   842  		panic(err)
   843  	}
   844  }
   845  
   846  func TestCustomOptionsDiscoveredInSameFile(t *testing.T) {
   847  	// Add option for every type to file
   848  	file := NewFile("foo.proto")
   849  
   850  	fileOpt := NewExtensionImported("file_foo", 54321, FieldTypeString(), fileOptionsDesc)
   851  	file.AddExtension(fileOpt)
   852  
   853  	msgOpt := NewExtensionImported("msg_foo", 54321, FieldTypeString(), msgOptionsDesc)
   854  	file.AddExtension(msgOpt)
   855  
   856  	fieldOpt := NewExtensionImported("field_foo", 54321, FieldTypeString(), fieldOptionsDesc)
   857  	file.AddExtension(fieldOpt)
   858  
   859  	oneofOpt := NewExtensionImported("oneof_foo", 54321, FieldTypeString(), oneofOptionsDesc)
   860  	file.AddExtension(oneofOpt)
   861  
   862  	extRangeOpt := NewExtensionImported("ext_range_foo", 54321, FieldTypeString(), extRangeOptionsDesc)
   863  	file.AddExtension(extRangeOpt)
   864  
   865  	enumOpt := NewExtensionImported("enum_foo", 54321, FieldTypeString(), enumOptionsDesc)
   866  	file.AddExtension(enumOpt)
   867  
   868  	enumValOpt := NewExtensionImported("enum_val_foo", 54321, FieldTypeString(), enumValOptionsDesc)
   869  	file.AddExtension(enumValOpt)
   870  
   871  	svcOpt := NewExtensionImported("svc_foo", 54321, FieldTypeString(), svcOptionsDesc)
   872  	file.AddExtension(svcOpt)
   873  
   874  	mtdOpt := NewExtensionImported("mtd_foo", 54321, FieldTypeString(), mtdOptionsDesc)
   875  	file.AddExtension(mtdOpt)
   876  
   877  	// Now we can test referring to these and making sure they show up correctly
   878  	// in built descriptors
   879  
   880  	t.Run("file options", func(t *testing.T) {
   881  		fb := clone(t, file)
   882  		fb.Options = &dpb.FileOptions{}
   883  		ext, err := fileOpt.Build()
   884  		testutil.Ok(t, err)
   885  		err = dynamic.SetExtension(fb.Options, ext, "fubar")
   886  		testutil.Ok(t, err)
   887  		checkBuildWithLocalExtensions(t, fb)
   888  	})
   889  
   890  	t.Run("message options", func(t *testing.T) {
   891  		mb := NewMessage("Foo")
   892  		mb.Options = &dpb.MessageOptions{}
   893  		ext, err := msgOpt.Build()
   894  		testutil.Ok(t, err)
   895  		err = dynamic.SetExtension(mb.Options, ext, "fubar")
   896  		testutil.Ok(t, err)
   897  
   898  		fb := clone(t, file)
   899  		fb.AddMessage(mb)
   900  		checkBuildWithLocalExtensions(t, mb)
   901  	})
   902  
   903  	t.Run("field options", func(t *testing.T) {
   904  		flb := NewField("foo", FieldTypeString())
   905  		flb.Options = &dpb.FieldOptions{}
   906  		// fields must be connected to a message
   907  		mb := NewMessage("Foo").AddField(flb)
   908  		ext, err := fieldOpt.Build()
   909  		testutil.Ok(t, err)
   910  		err = dynamic.SetExtension(flb.Options, ext, "fubar")
   911  		testutil.Ok(t, err)
   912  
   913  		fb := clone(t, file)
   914  		fb.AddMessage(mb)
   915  		checkBuildWithLocalExtensions(t, flb)
   916  	})
   917  
   918  	t.Run("oneof options", func(t *testing.T) {
   919  		oob := NewOneOf("oo")
   920  		oob.Options = &dpb.OneofOptions{}
   921  		// oneofs must be connected to a message
   922  		mb := NewMessage("Foo").AddOneOf(oob)
   923  		ext, err := oneofOpt.Build()
   924  		testutil.Ok(t, err)
   925  		err = dynamic.SetExtension(oob.Options, ext, "fubar")
   926  		testutil.Ok(t, err)
   927  
   928  		fb := clone(t, file)
   929  		fb.AddMessage(mb)
   930  		checkBuildWithLocalExtensions(t, oob)
   931  	})
   932  
   933  	t.Run("extension range options", func(t *testing.T) {
   934  		var erOpts dpb.ExtensionRangeOptions
   935  		ext, err := extRangeOpt.Build()
   936  		testutil.Ok(t, err)
   937  		err = dynamic.SetExtension(&erOpts, ext, "fubar")
   938  		testutil.Ok(t, err)
   939  		mb := NewMessage("foo").AddExtensionRangeWithOptions(100, 200, &erOpts)
   940  
   941  		fb := clone(t, file)
   942  		fb.AddMessage(mb)
   943  		checkBuildWithLocalExtensions(t, mb)
   944  	})
   945  
   946  	t.Run("enum options", func(t *testing.T) {
   947  		eb := NewEnum("Foo")
   948  		eb.Options = &dpb.EnumOptions{}
   949  		ext, err := enumOpt.Build()
   950  		testutil.Ok(t, err)
   951  		err = dynamic.SetExtension(eb.Options, ext, "fubar")
   952  		testutil.Ok(t, err)
   953  
   954  		fb := clone(t, file)
   955  		fb.AddEnum(eb)
   956  		checkBuildWithLocalExtensions(t, eb)
   957  	})
   958  
   959  	t.Run("enum val options", func(t *testing.T) {
   960  		evb := NewEnumValue("FOO")
   961  		// enum values must be connected to an enum
   962  		eb := NewEnum("Foo").AddValue(evb)
   963  		evb.Options = &dpb.EnumValueOptions{}
   964  		ext, err := enumValOpt.Build()
   965  		testutil.Ok(t, err)
   966  		err = dynamic.SetExtension(evb.Options, ext, "fubar")
   967  		testutil.Ok(t, err)
   968  
   969  		fb := clone(t, file)
   970  		fb.AddEnum(eb)
   971  		checkBuildWithLocalExtensions(t, evb)
   972  	})
   973  
   974  	t.Run("service options", func(t *testing.T) {
   975  		sb := NewService("Foo")
   976  		sb.Options = &dpb.ServiceOptions{}
   977  		ext, err := svcOpt.Build()
   978  		testutil.Ok(t, err)
   979  		err = dynamic.SetExtension(sb.Options, ext, "fubar")
   980  		testutil.Ok(t, err)
   981  
   982  		fb := clone(t, file)
   983  		fb.AddService(sb)
   984  		checkBuildWithLocalExtensions(t, sb)
   985  	})
   986  
   987  	t.Run("method options", func(t *testing.T) {
   988  		req := NewMessage("Request")
   989  		resp := NewMessage("Response")
   990  		mtb := NewMethod("Foo",
   991  			RpcTypeMessage(req, false),
   992  			RpcTypeMessage(resp, false))
   993  		// methods must be connected to a service
   994  		sb := NewService("Bar").AddMethod(mtb)
   995  		mtb.Options = &dpb.MethodOptions{}
   996  		ext, err := mtdOpt.Build()
   997  		testutil.Ok(t, err)
   998  		err = dynamic.SetExtension(mtb.Options, ext, "fubar")
   999  		testutil.Ok(t, err)
  1000  
  1001  		fb := clone(t, file)
  1002  		fb.AddService(sb).AddMessage(req).AddMessage(resp)
  1003  		checkBuildWithLocalExtensions(t, mtb)
  1004  	})
  1005  }
  1006  
  1007  func checkBuildWithLocalExtensions(t *testing.T, builder Builder) {
  1008  	// requiring options and succeeding (since they are defined locally)
  1009  	var opts BuilderOptions
  1010  	opts.RequireInterpretedOptions = true
  1011  	d, err := opts.Build(builder)
  1012  	testutil.Ok(t, err)
  1013  	// since they are defined locally, no extra imports
  1014  	testutil.Eq(t, []string{"google/protobuf/descriptor.proto"}, d.GetFile().AsFileDescriptorProto().GetDependency())
  1015  }
  1016  
  1017  func TestCustomOptionsDiscoveredInDependencies(t *testing.T) {
  1018  	// Add option for every type to file
  1019  	file := NewFile("options.proto")
  1020  
  1021  	fileOpt := NewExtensionImported("file_foo", 54321, FieldTypeString(), fileOptionsDesc)
  1022  	file.AddExtension(fileOpt)
  1023  
  1024  	msgOpt := NewExtensionImported("msg_foo", 54321, FieldTypeString(), msgOptionsDesc)
  1025  	file.AddExtension(msgOpt)
  1026  
  1027  	fieldOpt := NewExtensionImported("field_foo", 54321, FieldTypeString(), fieldOptionsDesc)
  1028  	file.AddExtension(fieldOpt)
  1029  
  1030  	oneofOpt := NewExtensionImported("oneof_foo", 54321, FieldTypeString(), oneofOptionsDesc)
  1031  	file.AddExtension(oneofOpt)
  1032  
  1033  	extRangeOpt := NewExtensionImported("ext_range_foo", 54321, FieldTypeString(), extRangeOptionsDesc)
  1034  	file.AddExtension(extRangeOpt)
  1035  
  1036  	enumOpt := NewExtensionImported("enum_foo", 54321, FieldTypeString(), enumOptionsDesc)
  1037  	file.AddExtension(enumOpt)
  1038  
  1039  	enumValOpt := NewExtensionImported("enum_val_foo", 54321, FieldTypeString(), enumValOptionsDesc)
  1040  	file.AddExtension(enumValOpt)
  1041  
  1042  	svcOpt := NewExtensionImported("svc_foo", 54321, FieldTypeString(), svcOptionsDesc)
  1043  	file.AddExtension(svcOpt)
  1044  
  1045  	mtdOpt := NewExtensionImported("mtd_foo", 54321, FieldTypeString(), mtdOptionsDesc)
  1046  	file.AddExtension(mtdOpt)
  1047  
  1048  	fileDesc, err := file.Build()
  1049  	testutil.Ok(t, err)
  1050  
  1051  	// Now we can test referring to these and making sure they show up correctly
  1052  	// in built descriptors
  1053  	for name, useBuilder := range map[string]bool{"descriptor": false, "builder": true} {
  1054  		newFile := func() *FileBuilder {
  1055  			fb := NewFile("foo.proto")
  1056  			if useBuilder {
  1057  				fb.AddDependency(file)
  1058  			} else {
  1059  				fb.AddImportedDependency(fileDesc)
  1060  			}
  1061  			return fb
  1062  		}
  1063  		t.Run(name, func(t *testing.T) {
  1064  			t.Run("file options", func(t *testing.T) {
  1065  				fb := newFile()
  1066  				fb.Options = &dpb.FileOptions{}
  1067  				ext, err := fileOpt.Build()
  1068  				testutil.Ok(t, err)
  1069  				err = dynamic.SetExtension(fb.Options, ext, "fubar")
  1070  				testutil.Ok(t, err)
  1071  				checkBuildWithImportedExtensions(t, fb)
  1072  			})
  1073  
  1074  			t.Run("message options", func(t *testing.T) {
  1075  				mb := NewMessage("Foo")
  1076  				mb.Options = &dpb.MessageOptions{}
  1077  				ext, err := msgOpt.Build()
  1078  				testutil.Ok(t, err)
  1079  				err = dynamic.SetExtension(mb.Options, ext, "fubar")
  1080  				testutil.Ok(t, err)
  1081  
  1082  				fb := newFile()
  1083  				fb.AddMessage(mb)
  1084  				checkBuildWithImportedExtensions(t, mb)
  1085  			})
  1086  
  1087  			t.Run("field options", func(t *testing.T) {
  1088  				flb := NewField("foo", FieldTypeString())
  1089  				flb.Options = &dpb.FieldOptions{}
  1090  				// fields must be connected to a message
  1091  				mb := NewMessage("Foo").AddField(flb)
  1092  				ext, err := fieldOpt.Build()
  1093  				testutil.Ok(t, err)
  1094  				err = dynamic.SetExtension(flb.Options, ext, "fubar")
  1095  				testutil.Ok(t, err)
  1096  
  1097  				fb := newFile()
  1098  				fb.AddMessage(mb)
  1099  				checkBuildWithImportedExtensions(t, flb)
  1100  			})
  1101  
  1102  			t.Run("oneof options", func(t *testing.T) {
  1103  				oob := NewOneOf("oo")
  1104  				oob.Options = &dpb.OneofOptions{}
  1105  				// oneofs must be connected to a message
  1106  				mb := NewMessage("Foo").AddOneOf(oob)
  1107  				ext, err := oneofOpt.Build()
  1108  				testutil.Ok(t, err)
  1109  				err = dynamic.SetExtension(oob.Options, ext, "fubar")
  1110  				testutil.Ok(t, err)
  1111  
  1112  				fb := newFile()
  1113  				fb.AddMessage(mb)
  1114  				checkBuildWithImportedExtensions(t, oob)
  1115  			})
  1116  
  1117  			t.Run("extension range options", func(t *testing.T) {
  1118  				var erOpts dpb.ExtensionRangeOptions
  1119  				ext, err := extRangeOpt.Build()
  1120  				testutil.Ok(t, err)
  1121  				err = dynamic.SetExtension(&erOpts, ext, "fubar")
  1122  				testutil.Ok(t, err)
  1123  				mb := NewMessage("foo").AddExtensionRangeWithOptions(100, 200, &erOpts)
  1124  
  1125  				fb := newFile()
  1126  				fb.AddMessage(mb)
  1127  				checkBuildWithImportedExtensions(t, mb)
  1128  			})
  1129  
  1130  			t.Run("enum options", func(t *testing.T) {
  1131  				eb := NewEnum("Foo")
  1132  				eb.Options = &dpb.EnumOptions{}
  1133  				ext, err := enumOpt.Build()
  1134  				testutil.Ok(t, err)
  1135  				err = dynamic.SetExtension(eb.Options, ext, "fubar")
  1136  				testutil.Ok(t, err)
  1137  
  1138  				fb := newFile()
  1139  				fb.AddEnum(eb)
  1140  				checkBuildWithImportedExtensions(t, eb)
  1141  			})
  1142  
  1143  			t.Run("enum val options", func(t *testing.T) {
  1144  				evb := NewEnumValue("FOO")
  1145  				// enum values must be connected to an enum
  1146  				eb := NewEnum("Foo").AddValue(evb)
  1147  				evb.Options = &dpb.EnumValueOptions{}
  1148  				ext, err := enumValOpt.Build()
  1149  				testutil.Ok(t, err)
  1150  				err = dynamic.SetExtension(evb.Options, ext, "fubar")
  1151  				testutil.Ok(t, err)
  1152  
  1153  				fb := newFile()
  1154  				fb.AddEnum(eb)
  1155  				checkBuildWithImportedExtensions(t, evb)
  1156  			})
  1157  
  1158  			t.Run("service options", func(t *testing.T) {
  1159  				sb := NewService("Foo")
  1160  				sb.Options = &dpb.ServiceOptions{}
  1161  				ext, err := svcOpt.Build()
  1162  				testutil.Ok(t, err)
  1163  				err = dynamic.SetExtension(sb.Options, ext, "fubar")
  1164  				testutil.Ok(t, err)
  1165  
  1166  				fb := newFile()
  1167  				fb.AddService(sb)
  1168  				checkBuildWithImportedExtensions(t, sb)
  1169  			})
  1170  
  1171  			t.Run("method options", func(t *testing.T) {
  1172  				req := NewMessage("Request")
  1173  				resp := NewMessage("Response")
  1174  				mtb := NewMethod("Foo",
  1175  					RpcTypeMessage(req, false),
  1176  					RpcTypeMessage(resp, false))
  1177  				// methods must be connected to a service
  1178  				sb := NewService("Bar").AddMethod(mtb)
  1179  				mtb.Options = &dpb.MethodOptions{}
  1180  				ext, err := mtdOpt.Build()
  1181  				testutil.Ok(t, err)
  1182  				err = dynamic.SetExtension(mtb.Options, ext, "fubar")
  1183  				testutil.Ok(t, err)
  1184  
  1185  				fb := newFile()
  1186  				fb.AddService(sb).AddMessage(req).AddMessage(resp)
  1187  				checkBuildWithImportedExtensions(t, mtb)
  1188  			})
  1189  		})
  1190  	}
  1191  }
  1192  
  1193  func checkBuildWithImportedExtensions(t *testing.T, builder Builder) {
  1194  	// requiring options and succeeding (since they are defined in explicit import)
  1195  	var opts BuilderOptions
  1196  	opts.RequireInterpretedOptions = true
  1197  	d, err := opts.Build(builder)
  1198  	testutil.Ok(t, err)
  1199  	// the only import is for the custom options
  1200  	testutil.Eq(t, []string{"options.proto"}, d.GetFile().AsFileDescriptorProto().GetDependency())
  1201  }
  1202  
  1203  func TestUseOfExtensionRegistry(t *testing.T) {
  1204  	// Add option for every type to extension registry
  1205  	var exts dynamic.ExtensionRegistry
  1206  
  1207  	fileOpt, err := NewExtensionImported("file_foo", 54321, FieldTypeString(), fileOptionsDesc).Build()
  1208  	testutil.Ok(t, err)
  1209  	err = exts.AddExtension(fileOpt)
  1210  	testutil.Ok(t, err)
  1211  
  1212  	msgOpt, err := NewExtensionImported("msg_foo", 54321, FieldTypeString(), msgOptionsDesc).Build()
  1213  	testutil.Ok(t, err)
  1214  	err = exts.AddExtension(msgOpt)
  1215  	testutil.Ok(t, err)
  1216  
  1217  	fieldOpt, err := NewExtensionImported("field_foo", 54321, FieldTypeString(), fieldOptionsDesc).Build()
  1218  	testutil.Ok(t, err)
  1219  	err = exts.AddExtension(fieldOpt)
  1220  	testutil.Ok(t, err)
  1221  
  1222  	oneofOpt, err := NewExtensionImported("oneof_foo", 54321, FieldTypeString(), oneofOptionsDesc).Build()
  1223  	testutil.Ok(t, err)
  1224  	err = exts.AddExtension(oneofOpt)
  1225  	testutil.Ok(t, err)
  1226  
  1227  	extRangeOpt, err := NewExtensionImported("ext_range_foo", 54321, FieldTypeString(), extRangeOptionsDesc).Build()
  1228  	testutil.Ok(t, err)
  1229  	err = exts.AddExtension(extRangeOpt)
  1230  	testutil.Ok(t, err)
  1231  
  1232  	enumOpt, err := NewExtensionImported("enum_foo", 54321, FieldTypeString(), enumOptionsDesc).Build()
  1233  	testutil.Ok(t, err)
  1234  	err = exts.AddExtension(enumOpt)
  1235  	testutil.Ok(t, err)
  1236  
  1237  	enumValOpt, err := NewExtensionImported("enum_val_foo", 54321, FieldTypeString(), enumValOptionsDesc).Build()
  1238  	testutil.Ok(t, err)
  1239  	err = exts.AddExtension(enumValOpt)
  1240  	testutil.Ok(t, err)
  1241  
  1242  	svcOpt, err := NewExtensionImported("svc_foo", 54321, FieldTypeString(), svcOptionsDesc).Build()
  1243  	testutil.Ok(t, err)
  1244  	err = exts.AddExtension(svcOpt)
  1245  	testutil.Ok(t, err)
  1246  
  1247  	mtdOpt, err := NewExtensionImported("mtd_foo", 54321, FieldTypeString(), mtdOptionsDesc).Build()
  1248  	testutil.Ok(t, err)
  1249  	err = exts.AddExtension(mtdOpt)
  1250  	testutil.Ok(t, err)
  1251  
  1252  	// Now we can test referring to these and making sure they show up correctly
  1253  	// in built descriptors
  1254  
  1255  	t.Run("file options", func(t *testing.T) {
  1256  		fb := NewFile("foo.proto")
  1257  		fb.Options = &dpb.FileOptions{}
  1258  		err = dynamic.SetExtension(fb.Options, fileOpt, "fubar")
  1259  		testutil.Ok(t, err)
  1260  		checkBuildWithExtensions(t, &exts, fileOpt.GetFile(), fb)
  1261  	})
  1262  
  1263  	t.Run("message options", func(t *testing.T) {
  1264  		mb := NewMessage("Foo")
  1265  		mb.Options = &dpb.MessageOptions{}
  1266  		err = dynamic.SetExtension(mb.Options, msgOpt, "fubar")
  1267  		testutil.Ok(t, err)
  1268  		checkBuildWithExtensions(t, &exts, msgOpt.GetFile(), mb)
  1269  	})
  1270  
  1271  	t.Run("field options", func(t *testing.T) {
  1272  		flb := NewField("foo", FieldTypeString())
  1273  		flb.Options = &dpb.FieldOptions{}
  1274  		// fields must be connected to a message
  1275  		NewMessage("Foo").AddField(flb)
  1276  		err = dynamic.SetExtension(flb.Options, fieldOpt, "fubar")
  1277  		testutil.Ok(t, err)
  1278  		checkBuildWithExtensions(t, &exts, fieldOpt.GetFile(), flb)
  1279  	})
  1280  
  1281  	t.Run("oneof options", func(t *testing.T) {
  1282  		oob := NewOneOf("oo")
  1283  		oob.Options = &dpb.OneofOptions{}
  1284  		// oneofs must be connected to a message
  1285  		NewMessage("Foo").AddOneOf(oob)
  1286  		err = dynamic.SetExtension(oob.Options, oneofOpt, "fubar")
  1287  		testutil.Ok(t, err)
  1288  		checkBuildWithExtensions(t, &exts, oneofOpt.GetFile(), oob)
  1289  	})
  1290  
  1291  	t.Run("extension range options", func(t *testing.T) {
  1292  		var erOpts dpb.ExtensionRangeOptions
  1293  		err = dynamic.SetExtension(&erOpts, extRangeOpt, "fubar")
  1294  		testutil.Ok(t, err)
  1295  		mb := NewMessage("foo").AddExtensionRangeWithOptions(100, 200, &erOpts)
  1296  		checkBuildWithExtensions(t, &exts, extRangeOpt.GetFile(), mb)
  1297  	})
  1298  
  1299  	t.Run("enum options", func(t *testing.T) {
  1300  		eb := NewEnum("Foo")
  1301  		eb.Options = &dpb.EnumOptions{}
  1302  		err = dynamic.SetExtension(eb.Options, enumOpt, "fubar")
  1303  		testutil.Ok(t, err)
  1304  		checkBuildWithExtensions(t, &exts, enumOpt.GetFile(), eb)
  1305  	})
  1306  
  1307  	t.Run("enum val options", func(t *testing.T) {
  1308  		evb := NewEnumValue("FOO")
  1309  		// enum values must be connected to an enum
  1310  		NewEnum("Foo").AddValue(evb)
  1311  		evb.Options = &dpb.EnumValueOptions{}
  1312  		err = dynamic.SetExtension(evb.Options, enumValOpt, "fubar")
  1313  		testutil.Ok(t, err)
  1314  		checkBuildWithExtensions(t, &exts, enumValOpt.GetFile(), evb)
  1315  	})
  1316  
  1317  	t.Run("service options", func(t *testing.T) {
  1318  		sb := NewService("Foo")
  1319  		sb.Options = &dpb.ServiceOptions{}
  1320  		err = dynamic.SetExtension(sb.Options, svcOpt, "fubar")
  1321  		testutil.Ok(t, err)
  1322  		checkBuildWithExtensions(t, &exts, svcOpt.GetFile(), sb)
  1323  	})
  1324  
  1325  	t.Run("method options", func(t *testing.T) {
  1326  		mtb := NewMethod("Foo",
  1327  			RpcTypeMessage(NewMessage("Request"), false),
  1328  			RpcTypeMessage(NewMessage("Response"), false))
  1329  		// methods must be connected to a service
  1330  		NewService("Bar").AddMethod(mtb)
  1331  		mtb.Options = &dpb.MethodOptions{}
  1332  		err = dynamic.SetExtension(mtb.Options, mtdOpt, "fubar")
  1333  		testutil.Ok(t, err)
  1334  		checkBuildWithExtensions(t, &exts, mtdOpt.GetFile(), mtb)
  1335  	})
  1336  }
  1337  
  1338  func checkBuildWithExtensions(t *testing.T, exts *dynamic.ExtensionRegistry, expected *desc.FileDescriptor, builder Builder) {
  1339  	// without interpreting custom option
  1340  	d, err := builder.BuildDescriptor()
  1341  	testutil.Ok(t, err)
  1342  	for _, dep := range d.GetFile().GetDependencies() {
  1343  		testutil.Neq(t, expected, dep)
  1344  	}
  1345  	numDeps := len(d.GetFile().GetDependencies())
  1346  
  1347  	// requiring options (and failing)
  1348  	var opts BuilderOptions
  1349  	opts.RequireInterpretedOptions = true
  1350  	_, err = opts.Build(builder)
  1351  	testutil.Require(t, err != nil)
  1352  
  1353  	// able to interpret options via extension registry
  1354  	opts.Extensions = exts
  1355  	d, err = opts.Build(builder)
  1356  	testutil.Ok(t, err)
  1357  	testutil.Eq(t, numDeps+1, len(d.GetFile().GetDependencies()))
  1358  	found := false
  1359  	for _, dep := range d.GetFile().GetDependencies() {
  1360  		if expected == dep {
  1361  			found = true
  1362  			break
  1363  		}
  1364  	}
  1365  	testutil.Require(t, found)
  1366  }
  1367  
  1368  func TestRemoveField(t *testing.T) {
  1369  	msg := NewMessage("FancyMessage").
  1370  		AddField(NewField("one", FieldTypeInt64())).
  1371  		AddField(NewField("two", FieldTypeString())).
  1372  		AddField(NewField("three", FieldTypeString()))
  1373  
  1374  	ok := msg.TryRemoveField("two")
  1375  	children := msg.GetChildren()
  1376  
  1377  	testutil.Require(t, ok)
  1378  	testutil.Eq(t, 2, len(children))
  1379  	testutil.Eq(t, "one", children[0].GetName())
  1380  	testutil.Eq(t, "three", children[1].GetName())
  1381  }
  1382  
  1383  func clone(t *testing.T, fb *FileBuilder) *FileBuilder {
  1384  	fd, err := fb.Build()
  1385  	testutil.Ok(t, err)
  1386  	fb, err = FromFile(fd)
  1387  	testutil.Ok(t, err)
  1388  	return fb
  1389  }