| // Copyright 2018 The Go Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style | 
 | // license that can be found in the LICENSE file. | 
 |  | 
 | package filedesc_test | 
 |  | 
 | import ( | 
 | 	"fmt" | 
 | 	"reflect" | 
 | 	"regexp" | 
 | 	"strconv" | 
 | 	"strings" | 
 | 	"testing" | 
 |  | 
 | 	"github.com/google/go-cmp/cmp" | 
 |  | 
 | 	detrand "google.golang.org/protobuf/internal/detrand" | 
 | 	"google.golang.org/protobuf/internal/filedesc" | 
 | 	"google.golang.org/protobuf/proto" | 
 | 	pdesc "google.golang.org/protobuf/reflect/protodesc" | 
 | 	pref "google.golang.org/protobuf/reflect/protoreflect" | 
 |  | 
 | 	"google.golang.org/protobuf/types/descriptorpb" | 
 | ) | 
 |  | 
 | func init() { | 
 | 	// Disable detrand to enable direct comparisons on outputs. | 
 | 	detrand.Disable() | 
 | } | 
 |  | 
 | // TODO: Test protodesc.NewFile with imported files. | 
 |  | 
 | func TestFile(t *testing.T) { | 
 | 	f1 := &descriptorpb.FileDescriptorProto{ | 
 | 		Syntax:  proto.String("proto2"), | 
 | 		Name:    proto.String("path/to/file.proto"), | 
 | 		Package: proto.String("test"), | 
 | 		Options: &descriptorpb.FileOptions{Deprecated: proto.Bool(true)}, | 
 | 		MessageType: []*descriptorpb.DescriptorProto{{ | 
 | 			Name: proto.String("A"), | 
 | 			Options: &descriptorpb.MessageOptions{ | 
 | 				Deprecated: proto.Bool(true), | 
 | 			}, | 
 | 		}, { | 
 | 			Name: proto.String("B"), | 
 | 			Field: []*descriptorpb.FieldDescriptorProto{{ | 
 | 				Name:         proto.String("field_one"), | 
 | 				Number:       proto.Int32(1), | 
 | 				Label:        descriptorpb.FieldDescriptorProto_Label(pref.Optional).Enum(), | 
 | 				Type:         descriptorpb.FieldDescriptorProto_Type(pref.StringKind).Enum(), | 
 | 				DefaultValue: proto.String("hello, \"world!\"\n"), | 
 | 				OneofIndex:   proto.Int32(0), | 
 | 			}, { | 
 | 				Name:         proto.String("field_two"), | 
 | 				JsonName:     proto.String("Field2"), | 
 | 				Number:       proto.Int32(2), | 
 | 				Label:        descriptorpb.FieldDescriptorProto_Label(pref.Optional).Enum(), | 
 | 				Type:         descriptorpb.FieldDescriptorProto_Type(pref.EnumKind).Enum(), | 
 | 				DefaultValue: proto.String("BAR"), | 
 | 				TypeName:     proto.String(".test.E1"), | 
 | 				OneofIndex:   proto.Int32(1), | 
 | 			}, { | 
 | 				Name:       proto.String("field_three"), | 
 | 				Number:     proto.Int32(3), | 
 | 				Label:      descriptorpb.FieldDescriptorProto_Label(pref.Optional).Enum(), | 
 | 				Type:       descriptorpb.FieldDescriptorProto_Type(pref.MessageKind).Enum(), | 
 | 				TypeName:   proto.String(".test.C"), | 
 | 				OneofIndex: proto.Int32(1), | 
 | 			}, { | 
 | 				Name:     proto.String("field_four"), | 
 | 				JsonName: proto.String("Field4"), | 
 | 				Number:   proto.Int32(4), | 
 | 				Label:    descriptorpb.FieldDescriptorProto_Label(pref.Repeated).Enum(), | 
 | 				Type:     descriptorpb.FieldDescriptorProto_Type(pref.MessageKind).Enum(), | 
 | 				TypeName: proto.String(".test.B.FieldFourEntry"), | 
 | 			}, { | 
 | 				Name:    proto.String("field_five"), | 
 | 				Number:  proto.Int32(5), | 
 | 				Label:   descriptorpb.FieldDescriptorProto_Label(pref.Repeated).Enum(), | 
 | 				Type:    descriptorpb.FieldDescriptorProto_Type(pref.Int32Kind).Enum(), | 
 | 				Options: &descriptorpb.FieldOptions{Packed: proto.Bool(true)}, | 
 | 			}, { | 
 | 				Name:   proto.String("field_six"), | 
 | 				Number: proto.Int32(6), | 
 | 				Label:  descriptorpb.FieldDescriptorProto_Label(pref.Required).Enum(), | 
 | 				Type:   descriptorpb.FieldDescriptorProto_Type(pref.BytesKind).Enum(), | 
 | 			}}, | 
 | 			OneofDecl: []*descriptorpb.OneofDescriptorProto{ | 
 | 				{ | 
 | 					Name: proto.String("O1"), | 
 | 					Options: &descriptorpb.OneofOptions{ | 
 | 						UninterpretedOption: []*descriptorpb.UninterpretedOption{ | 
 | 							{StringValue: []byte("option")}, | 
 | 						}, | 
 | 					}, | 
 | 				}, | 
 | 				{Name: proto.String("O2")}, | 
 | 			}, | 
 | 			ReservedName: []string{"fizz", "buzz"}, | 
 | 			ReservedRange: []*descriptorpb.DescriptorProto_ReservedRange{ | 
 | 				{Start: proto.Int32(100), End: proto.Int32(200)}, | 
 | 				{Start: proto.Int32(300), End: proto.Int32(301)}, | 
 | 			}, | 
 | 			ExtensionRange: []*descriptorpb.DescriptorProto_ExtensionRange{ | 
 | 				{Start: proto.Int32(1000), End: proto.Int32(2000)}, | 
 | 				{Start: proto.Int32(3000), End: proto.Int32(3001), Options: new(descriptorpb.ExtensionRangeOptions)}, | 
 | 			}, | 
 | 			NestedType: []*descriptorpb.DescriptorProto{{ | 
 | 				Name: proto.String("FieldFourEntry"), | 
 | 				Field: []*descriptorpb.FieldDescriptorProto{{ | 
 | 					Name:   proto.String("key"), | 
 | 					Number: proto.Int32(1), | 
 | 					Label:  descriptorpb.FieldDescriptorProto_Label(pref.Optional).Enum(), | 
 | 					Type:   descriptorpb.FieldDescriptorProto_Type(pref.StringKind).Enum(), | 
 | 				}, { | 
 | 					Name:     proto.String("value"), | 
 | 					Number:   proto.Int32(2), | 
 | 					Label:    descriptorpb.FieldDescriptorProto_Label(pref.Optional).Enum(), | 
 | 					Type:     descriptorpb.FieldDescriptorProto_Type(pref.MessageKind).Enum(), | 
 | 					TypeName: proto.String(".test.B"), | 
 | 				}}, | 
 | 				Options: &descriptorpb.MessageOptions{ | 
 | 					MapEntry: proto.Bool(true), | 
 | 				}, | 
 | 			}}, | 
 | 		}, { | 
 | 			Name: proto.String("C"), | 
 | 			NestedType: []*descriptorpb.DescriptorProto{{ | 
 | 				Name: proto.String("A"), | 
 | 				Field: []*descriptorpb.FieldDescriptorProto{{ | 
 | 					Name:         proto.String("F"), | 
 | 					Number:       proto.Int32(1), | 
 | 					Label:        descriptorpb.FieldDescriptorProto_Label(pref.Required).Enum(), | 
 | 					Type:         descriptorpb.FieldDescriptorProto_Type(pref.BytesKind).Enum(), | 
 | 					DefaultValue: proto.String(`dead\276\357`), | 
 | 				}}, | 
 | 			}}, | 
 | 			EnumType: []*descriptorpb.EnumDescriptorProto{{ | 
 | 				Name: proto.String("E1"), | 
 | 				Value: []*descriptorpb.EnumValueDescriptorProto{ | 
 | 					{Name: proto.String("FOO"), Number: proto.Int32(0)}, | 
 | 					{Name: proto.String("BAR"), Number: proto.Int32(1)}, | 
 | 				}, | 
 | 			}}, | 
 | 			Extension: []*descriptorpb.FieldDescriptorProto{{ | 
 | 				Name:     proto.String("X"), | 
 | 				Number:   proto.Int32(1000), | 
 | 				Label:    descriptorpb.FieldDescriptorProto_Label(pref.Repeated).Enum(), | 
 | 				Type:     descriptorpb.FieldDescriptorProto_Type(pref.MessageKind).Enum(), | 
 | 				TypeName: proto.String(".test.C"), | 
 | 				Extendee: proto.String(".test.B"), | 
 | 			}}, | 
 | 		}}, | 
 | 		EnumType: []*descriptorpb.EnumDescriptorProto{{ | 
 | 			Name:    proto.String("E1"), | 
 | 			Options: &descriptorpb.EnumOptions{Deprecated: proto.Bool(true)}, | 
 | 			Value: []*descriptorpb.EnumValueDescriptorProto{ | 
 | 				{ | 
 | 					Name:    proto.String("FOO"), | 
 | 					Number:  proto.Int32(0), | 
 | 					Options: &descriptorpb.EnumValueOptions{Deprecated: proto.Bool(true)}, | 
 | 				}, | 
 | 				{Name: proto.String("BAR"), Number: proto.Int32(1)}, | 
 | 			}, | 
 | 			ReservedName: []string{"FIZZ", "BUZZ"}, | 
 | 			ReservedRange: []*descriptorpb.EnumDescriptorProto_EnumReservedRange{ | 
 | 				{Start: proto.Int32(10), End: proto.Int32(19)}, | 
 | 				{Start: proto.Int32(30), End: proto.Int32(30)}, | 
 | 			}, | 
 | 		}}, | 
 | 		Extension: []*descriptorpb.FieldDescriptorProto{{ | 
 | 			Name:     proto.String("X"), | 
 | 			Number:   proto.Int32(1000), | 
 | 			Label:    descriptorpb.FieldDescriptorProto_Label(pref.Repeated).Enum(), | 
 | 			Type:     descriptorpb.FieldDescriptorProto_Type(pref.EnumKind).Enum(), | 
 | 			Options:  &descriptorpb.FieldOptions{Packed: proto.Bool(true)}, | 
 | 			TypeName: proto.String(".test.E1"), | 
 | 			Extendee: proto.String(".test.B"), | 
 | 		}}, | 
 | 		Service: []*descriptorpb.ServiceDescriptorProto{{ | 
 | 			Name:    proto.String("S"), | 
 | 			Options: &descriptorpb.ServiceOptions{Deprecated: proto.Bool(true)}, | 
 | 			Method: []*descriptorpb.MethodDescriptorProto{{ | 
 | 				Name:            proto.String("M"), | 
 | 				InputType:       proto.String(".test.A"), | 
 | 				OutputType:      proto.String(".test.C.A"), | 
 | 				ClientStreaming: proto.Bool(true), | 
 | 				ServerStreaming: proto.Bool(true), | 
 | 				Options:         &descriptorpb.MethodOptions{Deprecated: proto.Bool(true)}, | 
 | 			}}, | 
 | 		}}, | 
 | 	} | 
 | 	fd1, err := pdesc.NewFile(f1, nil) | 
 | 	if err != nil { | 
 | 		t.Fatalf("protodesc.NewFile() error: %v", err) | 
 | 	} | 
 |  | 
 | 	b, err := proto.Marshal(f1) | 
 | 	if err != nil { | 
 | 		t.Fatalf("proto.Marshal() error: %v", err) | 
 | 	} | 
 | 	fd2 := filedesc.Builder{RawDescriptor: b}.Build().File | 
 |  | 
 | 	tests := []struct { | 
 | 		name string | 
 | 		desc pref.FileDescriptor | 
 | 	}{ | 
 | 		{"protodesc.NewFile", fd1}, | 
 | 		{"filedesc.Builder.Build", fd2}, | 
 | 	} | 
 | 	for _, tt := range tests { | 
 | 		tt := tt | 
 | 		t.Run(tt.name, func(t *testing.T) { | 
 | 			// Run sub-tests in parallel to induce potential races. | 
 | 			for i := 0; i < 2; i++ { | 
 | 				t.Run("Accessors", func(t *testing.T) { t.Parallel(); testFileAccessors(t, tt.desc) }) | 
 | 				t.Run("Format", func(t *testing.T) { t.Parallel(); testFileFormat(t, tt.desc) }) | 
 | 			} | 
 | 		}) | 
 | 	} | 
 | } | 
 |  | 
 | func testFileAccessors(t *testing.T, fd pref.FileDescriptor) { | 
 | 	// Represent the descriptor as a map where each key is an accessor method | 
 | 	// and the value is either the wanted tail value or another accessor map. | 
 | 	type M = map[string]interface{} | 
 | 	want := M{ | 
 | 		"Parent":        nil, | 
 | 		"Index":         0, | 
 | 		"Syntax":        pref.Proto2, | 
 | 		"Name":          pref.Name("test"), | 
 | 		"FullName":      pref.FullName("test"), | 
 | 		"Path":          "path/to/file.proto", | 
 | 		"Package":       pref.FullName("test"), | 
 | 		"IsPlaceholder": false, | 
 | 		"Options":       &descriptorpb.FileOptions{Deprecated: proto.Bool(true)}, | 
 | 		"Messages": M{ | 
 | 			"Len": 3, | 
 | 			"Get:0": M{ | 
 | 				"Parent":        M{"FullName": pref.FullName("test")}, | 
 | 				"Index":         0, | 
 | 				"Syntax":        pref.Proto2, | 
 | 				"Name":          pref.Name("A"), | 
 | 				"FullName":      pref.FullName("test.A"), | 
 | 				"IsPlaceholder": false, | 
 | 				"IsMapEntry":    false, | 
 | 				"Options": &descriptorpb.MessageOptions{ | 
 | 					Deprecated: proto.Bool(true), | 
 | 				}, | 
 | 				"Oneofs":          M{"Len": 0}, | 
 | 				"RequiredNumbers": M{"Len": 0}, | 
 | 				"ExtensionRanges": M{"Len": 0}, | 
 | 				"Messages":        M{"Len": 0}, | 
 | 				"Enums":           M{"Len": 0}, | 
 | 				"Extensions":      M{"Len": 0}, | 
 | 			}, | 
 | 			"ByName:B": M{ | 
 | 				"Name":  pref.Name("B"), | 
 | 				"Index": 1, | 
 | 				"Fields": M{ | 
 | 					"Len":                  6, | 
 | 					"ByJSONName:field_one": nil, | 
 | 					"ByJSONName:fieldOne": M{ | 
 | 						"Name":              pref.Name("field_one"), | 
 | 						"Index":             0, | 
 | 						"JSONName":          "fieldOne", | 
 | 						"Default":           "hello, \"world!\"\n", | 
 | 						"ContainingOneof":   M{"Name": pref.Name("O1"), "IsPlaceholder": false}, | 
 | 						"ContainingMessage": M{"FullName": pref.FullName("test.B")}, | 
 | 					}, | 
 | 					"ByJSONName:fieldTwo": nil, | 
 | 					"ByJSONName:Field2": M{ | 
 | 						"Name":            pref.Name("field_two"), | 
 | 						"Index":           1, | 
 | 						"HasJSONName":     true, | 
 | 						"JSONName":        "Field2", | 
 | 						"Default":         pref.EnumNumber(1), | 
 | 						"ContainingOneof": M{"Name": pref.Name("O2"), "IsPlaceholder": false}, | 
 | 					}, | 
 | 					"ByName:fieldThree": nil, | 
 | 					"ByName:field_three": M{ | 
 | 						"IsExtension":       false, | 
 | 						"IsMap":             false, | 
 | 						"MapKey":            nil, | 
 | 						"MapValue":          nil, | 
 | 						"Message":           M{"FullName": pref.FullName("test.C"), "IsPlaceholder": false}, | 
 | 						"ContainingOneof":   M{"Name": pref.Name("O2"), "IsPlaceholder": false}, | 
 | 						"ContainingMessage": M{"FullName": pref.FullName("test.B")}, | 
 | 					}, | 
 | 					"ByNumber:12": nil, | 
 | 					"ByNumber:4": M{ | 
 | 						"Cardinality": pref.Repeated, | 
 | 						"IsExtension": false, | 
 | 						"IsList":      false, | 
 | 						"IsMap":       true, | 
 | 						"MapKey":      M{"Kind": pref.StringKind}, | 
 | 						"MapValue":    M{"Kind": pref.MessageKind, "Message": M{"FullName": pref.FullName("test.B")}}, | 
 | 						"Default":     nil, | 
 | 						"Message":     M{"FullName": pref.FullName("test.B.FieldFourEntry"), "IsPlaceholder": false}, | 
 | 					}, | 
 | 					"ByNumber:5": M{ | 
 | 						"Cardinality": pref.Repeated, | 
 | 						"Kind":        pref.Int32Kind, | 
 | 						"IsPacked":    true, | 
 | 						"IsList":      true, | 
 | 						"IsMap":       false, | 
 | 						"Default":     nil, | 
 | 					}, | 
 | 					"ByNumber:6": M{ | 
 | 						"Cardinality":     pref.Required, | 
 | 						"Default":         []byte(nil), | 
 | 						"ContainingOneof": nil, | 
 | 					}, | 
 | 				}, | 
 | 				"Oneofs": M{ | 
 | 					"Len":       2, | 
 | 					"ByName:O0": nil, | 
 | 					"ByName:O1": M{ | 
 | 						"FullName": pref.FullName("test.B.O1"), | 
 | 						"Index":    0, | 
 | 						"Options": &descriptorpb.OneofOptions{ | 
 | 							UninterpretedOption: []*descriptorpb.UninterpretedOption{ | 
 | 								{StringValue: []byte("option")}, | 
 | 							}, | 
 | 						}, | 
 | 						"Fields": M{ | 
 | 							"Len":   1, | 
 | 							"Get:0": M{"FullName": pref.FullName("test.B.field_one")}, | 
 | 						}, | 
 | 					}, | 
 | 					"Get:1": M{ | 
 | 						"FullName": pref.FullName("test.B.O2"), | 
 | 						"Index":    1, | 
 | 						"Fields": M{ | 
 | 							"Len":              2, | 
 | 							"ByName:field_two": M{"Name": pref.Name("field_two")}, | 
 | 							"Get:1":            M{"Name": pref.Name("field_three")}, | 
 | 						}, | 
 | 					}, | 
 | 				}, | 
 | 				"ReservedNames": M{ | 
 | 					"Len":         2, | 
 | 					"Get:0":       pref.Name("fizz"), | 
 | 					"Has:buzz":    true, | 
 | 					"Has:noexist": false, | 
 | 				}, | 
 | 				"ReservedRanges": M{ | 
 | 					"Len":     2, | 
 | 					"Get:0":   [2]pref.FieldNumber{100, 200}, | 
 | 					"Has:99":  false, | 
 | 					"Has:100": true, | 
 | 					"Has:150": true, | 
 | 					"Has:199": true, | 
 | 					"Has:200": false, | 
 | 					"Has:300": true, | 
 | 					"Has:301": false, | 
 | 				}, | 
 | 				"RequiredNumbers": M{ | 
 | 					"Len":   1, | 
 | 					"Get:0": pref.FieldNumber(6), | 
 | 					"Has:1": false, | 
 | 					"Has:6": true, | 
 | 				}, | 
 | 				"ExtensionRanges": M{ | 
 | 					"Len":      2, | 
 | 					"Get:0":    [2]pref.FieldNumber{1000, 2000}, | 
 | 					"Has:999":  false, | 
 | 					"Has:1000": true, | 
 | 					"Has:1500": true, | 
 | 					"Has:1999": true, | 
 | 					"Has:2000": false, | 
 | 					"Has:3000": true, | 
 | 					"Has:3001": false, | 
 | 				}, | 
 | 				"ExtensionRangeOptions:0": (*descriptorpb.ExtensionRangeOptions)(nil), | 
 | 				"ExtensionRangeOptions:1": new(descriptorpb.ExtensionRangeOptions), | 
 | 				"Messages": M{ | 
 | 					"Get:0": M{ | 
 | 						"Fields": M{ | 
 | 							"Len": 2, | 
 | 							"ByNumber:1": M{ | 
 | 								"Parent":            M{"FullName": pref.FullName("test.B.FieldFourEntry")}, | 
 | 								"Index":             0, | 
 | 								"Name":              pref.Name("key"), | 
 | 								"FullName":          pref.FullName("test.B.FieldFourEntry.key"), | 
 | 								"Number":            pref.FieldNumber(1), | 
 | 								"Cardinality":       pref.Optional, | 
 | 								"Kind":              pref.StringKind, | 
 | 								"Options":           (*descriptorpb.FieldOptions)(nil), | 
 | 								"HasJSONName":       false, | 
 | 								"JSONName":          "key", | 
 | 								"IsPacked":          false, | 
 | 								"IsList":            false, | 
 | 								"IsMap":             false, | 
 | 								"IsExtension":       false, | 
 | 								"IsWeak":            false, | 
 | 								"Default":           "", | 
 | 								"ContainingOneof":   nil, | 
 | 								"ContainingMessage": M{"FullName": pref.FullName("test.B.FieldFourEntry")}, | 
 | 								"Message":           nil, | 
 | 								"Enum":              nil, | 
 | 							}, | 
 | 							"ByNumber:2": M{ | 
 | 								"Parent":            M{"FullName": pref.FullName("test.B.FieldFourEntry")}, | 
 | 								"Index":             1, | 
 | 								"Name":              pref.Name("value"), | 
 | 								"FullName":          pref.FullName("test.B.FieldFourEntry.value"), | 
 | 								"Number":            pref.FieldNumber(2), | 
 | 								"Cardinality":       pref.Optional, | 
 | 								"Kind":              pref.MessageKind, | 
 | 								"JSONName":          "value", | 
 | 								"IsPacked":          false, | 
 | 								"IsList":            false, | 
 | 								"IsMap":             false, | 
 | 								"IsExtension":       false, | 
 | 								"IsWeak":            false, | 
 | 								"Default":           nil, | 
 | 								"ContainingOneof":   nil, | 
 | 								"ContainingMessage": M{"FullName": pref.FullName("test.B.FieldFourEntry")}, | 
 | 								"Message":           M{"FullName": pref.FullName("test.B"), "IsPlaceholder": false}, | 
 | 								"Enum":              nil, | 
 | 							}, | 
 | 							"ByNumber:3": nil, | 
 | 						}, | 
 | 					}, | 
 | 				}, | 
 | 			}, | 
 | 			"Get:2": M{ | 
 | 				"Name":  pref.Name("C"), | 
 | 				"Index": 2, | 
 | 				"Messages": M{ | 
 | 					"Len":   1, | 
 | 					"Get:0": M{"FullName": pref.FullName("test.C.A")}, | 
 | 				}, | 
 | 				"Enums": M{ | 
 | 					"Len":   1, | 
 | 					"Get:0": M{"FullName": pref.FullName("test.C.E1")}, | 
 | 				}, | 
 | 				"Extensions": M{ | 
 | 					"Len":   1, | 
 | 					"Get:0": M{"FullName": pref.FullName("test.C.X")}, | 
 | 				}, | 
 | 			}, | 
 | 		}, | 
 | 		"Enums": M{ | 
 | 			"Len": 1, | 
 | 			"Get:0": M{ | 
 | 				"Name":    pref.Name("E1"), | 
 | 				"Options": &descriptorpb.EnumOptions{Deprecated: proto.Bool(true)}, | 
 | 				"Values": M{ | 
 | 					"Len":        2, | 
 | 					"ByName:Foo": nil, | 
 | 					"ByName:FOO": M{ | 
 | 						"FullName": pref.FullName("test.FOO"), | 
 | 						"Options":  &descriptorpb.EnumValueOptions{Deprecated: proto.Bool(true)}, | 
 | 					}, | 
 | 					"ByNumber:2": nil, | 
 | 					"ByNumber:1": M{"FullName": pref.FullName("test.BAR")}, | 
 | 				}, | 
 | 				"ReservedNames": M{ | 
 | 					"Len":         2, | 
 | 					"Get:0":       pref.Name("FIZZ"), | 
 | 					"Has:BUZZ":    true, | 
 | 					"Has:NOEXIST": false, | 
 | 				}, | 
 | 				"ReservedRanges": M{ | 
 | 					"Len":    2, | 
 | 					"Get:0":  [2]pref.EnumNumber{10, 19}, | 
 | 					"Has:9":  false, | 
 | 					"Has:10": true, | 
 | 					"Has:15": true, | 
 | 					"Has:19": true, | 
 | 					"Has:20": false, | 
 | 					"Has:30": true, | 
 | 					"Has:31": false, | 
 | 				}, | 
 | 			}, | 
 | 		}, | 
 | 		"Extensions": M{ | 
 | 			"Len": 1, | 
 | 			"ByName:X": M{ | 
 | 				"Name":              pref.Name("X"), | 
 | 				"Number":            pref.FieldNumber(1000), | 
 | 				"Cardinality":       pref.Repeated, | 
 | 				"Kind":              pref.EnumKind, | 
 | 				"IsExtension":       true, | 
 | 				"IsPacked":          true, | 
 | 				"IsList":            true, | 
 | 				"IsMap":             false, | 
 | 				"MapKey":            nil, | 
 | 				"MapValue":          nil, | 
 | 				"ContainingOneof":   nil, | 
 | 				"ContainingMessage": M{"FullName": pref.FullName("test.B"), "IsPlaceholder": false}, | 
 | 				"Enum":              M{"FullName": pref.FullName("test.E1"), "IsPlaceholder": false}, | 
 | 				"Options":           &descriptorpb.FieldOptions{Packed: proto.Bool(true)}, | 
 | 			}, | 
 | 		}, | 
 | 		"Services": M{ | 
 | 			"Len":      1, | 
 | 			"ByName:s": nil, | 
 | 			"ByName:S": M{ | 
 | 				"Parent":   M{"FullName": pref.FullName("test")}, | 
 | 				"Name":     pref.Name("S"), | 
 | 				"FullName": pref.FullName("test.S"), | 
 | 				"Options":  &descriptorpb.ServiceOptions{Deprecated: proto.Bool(true)}, | 
 | 				"Methods": M{ | 
 | 					"Len": 1, | 
 | 					"Get:0": M{ | 
 | 						"Parent":            M{"FullName": pref.FullName("test.S")}, | 
 | 						"Name":              pref.Name("M"), | 
 | 						"FullName":          pref.FullName("test.S.M"), | 
 | 						"Input":             M{"FullName": pref.FullName("test.A"), "IsPlaceholder": false}, | 
 | 						"Output":            M{"FullName": pref.FullName("test.C.A"), "IsPlaceholder": false}, | 
 | 						"IsStreamingClient": true, | 
 | 						"IsStreamingServer": true, | 
 | 						"Options":           &descriptorpb.MethodOptions{Deprecated: proto.Bool(true)}, | 
 | 					}, | 
 | 				}, | 
 | 			}, | 
 | 		}, | 
 | 	} | 
 | 	checkAccessors(t, "", reflect.ValueOf(fd), want) | 
 | } | 
 | func checkAccessors(t *testing.T, p string, rv reflect.Value, want map[string]interface{}) { | 
 | 	p0 := p | 
 | 	defer func() { | 
 | 		if ex := recover(); ex != nil { | 
 | 			t.Errorf("panic at %v: %v", p, ex) | 
 | 		} | 
 | 	}() | 
 |  | 
 | 	if rv.Interface() == nil { | 
 | 		t.Errorf("%v is nil, want non-nil", p) | 
 | 		return | 
 | 	} | 
 | 	for s, v := range want { | 
 | 		// Call the accessor method. | 
 | 		p = p0 + "." + s | 
 | 		var rets []reflect.Value | 
 | 		if i := strings.IndexByte(s, ':'); i >= 0 { | 
 | 			// Accessor method takes in a single argument, which is encoded | 
 | 			// after the accessor name, separated by a ':' delimiter. | 
 | 			fnc := rv.MethodByName(s[:i]) | 
 | 			arg := reflect.New(fnc.Type().In(0)).Elem() | 
 | 			s = s[i+len(":"):] | 
 | 			switch arg.Kind() { | 
 | 			case reflect.String: | 
 | 				arg.SetString(s) | 
 | 			case reflect.Int32, reflect.Int: | 
 | 				n, _ := strconv.ParseInt(s, 0, 64) | 
 | 				arg.SetInt(n) | 
 | 			} | 
 | 			rets = fnc.Call([]reflect.Value{arg}) | 
 | 		} else { | 
 | 			rets = rv.MethodByName(s).Call(nil) | 
 | 		} | 
 |  | 
 | 		// Check that (val, ok) pattern is internally consistent. | 
 | 		if len(rets) == 2 { | 
 | 			if rets[0].IsNil() && rets[1].Bool() { | 
 | 				t.Errorf("%v = (nil, true), want (nil, false)", p) | 
 | 			} | 
 | 			if !rets[0].IsNil() && !rets[1].Bool() { | 
 | 				t.Errorf("%v = (non-nil, false), want (non-nil, true)", p) | 
 | 			} | 
 | 		} | 
 |  | 
 | 		// Check that the accessor output matches. | 
 | 		if want, ok := v.(map[string]interface{}); ok { | 
 | 			checkAccessors(t, p, rets[0], want) | 
 | 			continue | 
 | 		} | 
 |  | 
 | 		got := rets[0].Interface() | 
 | 		if pv, ok := got.(pref.Value); ok { | 
 | 			got = pv.Interface() | 
 | 		} | 
 |  | 
 | 		// Compare with proto.Equal if possible. | 
 | 		gotMsg, gotMsgOK := got.(proto.Message) | 
 | 		wantMsg, wantMsgOK := v.(proto.Message) | 
 | 		if gotMsgOK && wantMsgOK { | 
 | 			gotNil := reflect.ValueOf(gotMsg).IsNil() | 
 | 			wantNil := reflect.ValueOf(wantMsg).IsNil() | 
 | 			switch { | 
 | 			case !gotNil && wantNil: | 
 | 				t.Errorf("%v = non-nil, want nil", p) | 
 | 			case gotNil && !wantNil: | 
 | 				t.Errorf("%v = nil, want non-nil", p) | 
 | 			case !proto.Equal(gotMsg, wantMsg): | 
 | 				t.Errorf("%v = %v, want %v", p, gotMsg, wantMsg) | 
 | 			} | 
 | 			continue | 
 | 		} | 
 |  | 
 | 		if want := v; !reflect.DeepEqual(got, want) { | 
 | 			t.Errorf("%v = %T(%v), want %T(%v)", p, got, got, want, want) | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | func testFileFormat(t *testing.T, fd pref.FileDescriptor) { | 
 | 	const want = `FileDescriptor{ | 
 | 	Syntax:  proto2 | 
 | 	Path:    "path/to/file.proto" | 
 | 	Package: test | 
 | 	Messages: [{ | 
 | 		Name: A | 
 | 	}, { | 
 | 		Name: B | 
 | 		Fields: [{ | 
 | 			Name:        field_one | 
 | 			Number:      1 | 
 | 			Cardinality: optional | 
 | 			Kind:        string | 
 | 			JSONName:    "fieldOne" | 
 | 			HasPresence: true | 
 | 			HasDefault:  true | 
 | 			Default:     "hello, \"world!\"\n" | 
 | 			Oneof:       O1 | 
 | 		}, { | 
 | 			Name:        field_two | 
 | 			Number:      2 | 
 | 			Cardinality: optional | 
 | 			Kind:        enum | 
 | 			HasJSONName: true | 
 | 			JSONName:    "Field2" | 
 | 			HasPresence: true | 
 | 			HasDefault:  true | 
 | 			Default:     1 | 
 | 			Oneof:       O2 | 
 | 			Enum:        test.E1 | 
 | 		}, { | 
 | 			Name:        field_three | 
 | 			Number:      3 | 
 | 			Cardinality: optional | 
 | 			Kind:        message | 
 | 			JSONName:    "fieldThree" | 
 | 			HasPresence: true | 
 | 			Oneof:       O2 | 
 | 			Message:     test.C | 
 | 		}, { | 
 | 			Name:        field_four | 
 | 			Number:      4 | 
 | 			Cardinality: repeated | 
 | 			Kind:        message | 
 | 			HasJSONName: true | 
 | 			JSONName:    "Field4" | 
 | 			IsMap:       true | 
 | 			MapKey:      string | 
 | 			MapValue:    test.B | 
 | 		}, { | 
 | 			Name:        field_five | 
 | 			Number:      5 | 
 | 			Cardinality: repeated | 
 | 			Kind:        int32 | 
 | 			JSONName:    "fieldFive" | 
 | 			IsPacked:    true | 
 | 			IsList:      true | 
 | 		}, { | 
 | 			Name:        field_six | 
 | 			Number:      6 | 
 | 			Cardinality: required | 
 | 			Kind:        bytes | 
 | 			JSONName:    "fieldSix" | 
 | 			HasPresence: true | 
 | 		}] | 
 | 		Oneofs: [{ | 
 | 			Name:   O1 | 
 | 			Fields: [field_one] | 
 | 		}, { | 
 | 			Name:   O2 | 
 | 			Fields: [field_two, field_three] | 
 | 		}] | 
 | 		ReservedNames:   [fizz, buzz] | 
 | 		ReservedRanges:  [100:200, 300] | 
 | 		RequiredNumbers: [6] | 
 | 		ExtensionRanges: [1000:2000, 3000] | 
 | 		Messages: [{ | 
 | 			Name:       FieldFourEntry | 
 | 			IsMapEntry: true | 
 | 			Fields: [{ | 
 | 				Name:        key | 
 | 				Number:      1 | 
 | 				Cardinality: optional | 
 | 				Kind:        string | 
 | 				JSONName:    "key" | 
 | 				HasPresence: true | 
 | 			}, { | 
 | 				Name:        value | 
 | 				Number:      2 | 
 | 				Cardinality: optional | 
 | 				Kind:        message | 
 | 				JSONName:    "value" | 
 | 				HasPresence: true | 
 | 				Message:     test.B | 
 | 			}] | 
 | 		}] | 
 | 	}, { | 
 | 		Name: C | 
 | 		Messages: [{ | 
 | 			Name: A | 
 | 			Fields: [{ | 
 | 				Name:        F | 
 | 				Number:      1 | 
 | 				Cardinality: required | 
 | 				Kind:        bytes | 
 | 				JSONName:    "F" | 
 | 				HasPresence: true | 
 | 				HasDefault:  true | 
 | 				Default:     "dead\xbe\xef" | 
 | 			}] | 
 | 			RequiredNumbers: [1] | 
 | 		}] | 
 | 		Enums: [{ | 
 | 			Name: E1 | 
 | 			Values: [ | 
 | 				{Name: FOO} | 
 | 				{Name: BAR, Number: 1} | 
 | 			] | 
 | 		}] | 
 | 		Extensions: [{ | 
 | 			Name:        X | 
 | 			Number:      1000 | 
 | 			Cardinality: repeated | 
 | 			Kind:        message | 
 | 			JSONName:    "X" | 
 | 			IsExtension: true | 
 | 			IsList:      true | 
 | 			Extendee:    test.B | 
 | 			Message:     test.C | 
 | 		}] | 
 | 	}] | 
 | 	Enums: [{ | 
 | 		Name: E1 | 
 | 		Values: [ | 
 | 			{Name: FOO} | 
 | 			{Name: BAR, Number: 1} | 
 | 		] | 
 | 		ReservedNames:  [FIZZ, BUZZ] | 
 | 		ReservedRanges: [10:20, 30] | 
 | 	}] | 
 | 	Extensions: [{ | 
 | 		Name:        X | 
 | 		Number:      1000 | 
 | 		Cardinality: repeated | 
 | 		Kind:        enum | 
 | 		JSONName:    "X" | 
 | 		IsExtension: true | 
 | 		IsPacked:    true | 
 | 		IsList:      true | 
 | 		Extendee:    test.B | 
 | 		Enum:        test.E1 | 
 | 	}] | 
 | 	Services: [{ | 
 | 		Name: S | 
 | 		Methods: [{ | 
 | 			Name:              M | 
 | 			Input:             test.A | 
 | 			Output:            test.C.A | 
 | 			IsStreamingClient: true | 
 | 			IsStreamingServer: true | 
 | 		}] | 
 | 	}] | 
 | }` | 
 | 	tests := []struct{ fmt, want string }{{"%v", compactMultiFormat(want)}, {"%+v", want}} | 
 | 	for _, tt := range tests { | 
 | 		got := fmt.Sprintf(tt.fmt, fd) | 
 | 		if diff := cmp.Diff(got, tt.want); diff != "" { | 
 | 			t.Errorf("fmt.Sprintf(%q, fd) mismatch (-got +want):\n%s", tt.fmt, diff) | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | // compactMultiFormat returns the single line form of a multi line output. | 
 | func compactMultiFormat(s string) string { | 
 | 	var b []byte | 
 | 	for _, s := range strings.Split(s, "\n") { | 
 | 		s = strings.TrimSpace(s) | 
 | 		s = regexp.MustCompile(": +").ReplaceAllString(s, ": ") | 
 | 		prevWord := len(b) > 0 && b[len(b)-1] != '[' && b[len(b)-1] != '{' | 
 | 		nextWord := len(s) > 0 && s[0] != ']' && s[0] != '}' | 
 | 		if prevWord && nextWord { | 
 | 			b = append(b, ", "...) | 
 | 		} | 
 | 		b = append(b, s...) | 
 | 	} | 
 | 	return string(b) | 
 | } |