123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781 |
- // 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"
- HasDefault: true
- Default: "hello, \"world!\"\n"
- Oneof: O1
- }, {
- Name: field_two
- Number: 2
- Cardinality: optional
- Kind: enum
- HasJSONName: true
- JSONName: "Field2"
- HasDefault: true
- Default: 1
- Oneof: O2
- Enum: test.E1
- }, {
- Name: field_three
- Number: 3
- Cardinality: optional
- Kind: message
- JSONName: "fieldThree"
- 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"
- }]
- 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"
- }, {
- Name: value
- Number: 2
- Cardinality: optional
- Kind: message
- JSONName: "value"
- Message: test.B
- }]
- }]
- }, {
- Name: C
- Messages: [{
- Name: A
- Fields: [{
- Name: F
- Number: 1
- Cardinality: required
- Kind: bytes
- JSONName: "F"
- 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"
- IsPacked: true
- IsExtension: 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)
- }
|