1
2
3
4
5 package filedesc_test
6
7 import (
8 "fmt"
9 "reflect"
10 "regexp"
11 "strconv"
12 "strings"
13 "testing"
14
15 "github.com/google/go-cmp/cmp"
16
17 "google.golang.org/protobuf/internal/detrand"
18 "google.golang.org/protobuf/internal/filedesc"
19 "google.golang.org/protobuf/proto"
20 "google.golang.org/protobuf/reflect/protodesc"
21 "google.golang.org/protobuf/reflect/protoreflect"
22
23 "google.golang.org/protobuf/types/descriptorpb"
24 )
25
26 func init() {
27
28 detrand.Disable()
29 }
30
31
32
33 func TestFile(t *testing.T) {
34 f1 := &descriptorpb.FileDescriptorProto{
35 Syntax: proto.String("proto2"),
36 Name: proto.String("path/to/file.proto"),
37 Package: proto.String("test"),
38 Options: &descriptorpb.FileOptions{Deprecated: proto.Bool(true)},
39 MessageType: []*descriptorpb.DescriptorProto{{
40 Name: proto.String("A"),
41 Options: &descriptorpb.MessageOptions{
42 Deprecated: proto.Bool(true),
43 },
44 }, {
45 Name: proto.String("B"),
46 Field: []*descriptorpb.FieldDescriptorProto{{
47 Name: proto.String("field_one"),
48 Number: proto.Int32(1),
49 Label: descriptorpb.FieldDescriptorProto_Label(protoreflect.Optional).Enum(),
50 Type: descriptorpb.FieldDescriptorProto_Type(protoreflect.StringKind).Enum(),
51 DefaultValue: proto.String("hello, \"world!\"\n"),
52 OneofIndex: proto.Int32(0),
53 }, {
54 Name: proto.String("field_two"),
55 JsonName: proto.String("Field2"),
56 Number: proto.Int32(2),
57 Label: descriptorpb.FieldDescriptorProto_Label(protoreflect.Optional).Enum(),
58 Type: descriptorpb.FieldDescriptorProto_Type(protoreflect.EnumKind).Enum(),
59 DefaultValue: proto.String("BAR"),
60 TypeName: proto.String(".test.E1"),
61 OneofIndex: proto.Int32(1),
62 }, {
63 Name: proto.String("field_three"),
64 Number: proto.Int32(3),
65 Label: descriptorpb.FieldDescriptorProto_Label(protoreflect.Optional).Enum(),
66 Type: descriptorpb.FieldDescriptorProto_Type(protoreflect.MessageKind).Enum(),
67 TypeName: proto.String(".test.C"),
68 OneofIndex: proto.Int32(1),
69 }, {
70 Name: proto.String("field_four"),
71 JsonName: proto.String("Field4"),
72 Number: proto.Int32(4),
73 Label: descriptorpb.FieldDescriptorProto_Label(protoreflect.Repeated).Enum(),
74 Type: descriptorpb.FieldDescriptorProto_Type(protoreflect.MessageKind).Enum(),
75 TypeName: proto.String(".test.B.FieldFourEntry"),
76 }, {
77 Name: proto.String("field_five"),
78 Number: proto.Int32(5),
79 Label: descriptorpb.FieldDescriptorProto_Label(protoreflect.Repeated).Enum(),
80 Type: descriptorpb.FieldDescriptorProto_Type(protoreflect.Int32Kind).Enum(),
81 Options: &descriptorpb.FieldOptions{Packed: proto.Bool(true)},
82 }, {
83 Name: proto.String("field_six"),
84 Number: proto.Int32(6),
85 Label: descriptorpb.FieldDescriptorProto_Label(protoreflect.Required).Enum(),
86 Type: descriptorpb.FieldDescriptorProto_Type(protoreflect.BytesKind).Enum(),
87 }},
88 OneofDecl: []*descriptorpb.OneofDescriptorProto{
89 {
90 Name: proto.String("O1"),
91 Options: &descriptorpb.OneofOptions{
92 UninterpretedOption: []*descriptorpb.UninterpretedOption{
93 {StringValue: []byte("option")},
94 },
95 },
96 },
97 {Name: proto.String("O2")},
98 },
99 ReservedName: []string{"fizz", "buzz"},
100 ReservedRange: []*descriptorpb.DescriptorProto_ReservedRange{
101 {Start: proto.Int32(100), End: proto.Int32(200)},
102 {Start: proto.Int32(300), End: proto.Int32(301)},
103 },
104 ExtensionRange: []*descriptorpb.DescriptorProto_ExtensionRange{
105 {Start: proto.Int32(1000), End: proto.Int32(2000)},
106 {Start: proto.Int32(3000), End: proto.Int32(3001), Options: new(descriptorpb.ExtensionRangeOptions)},
107 },
108 NestedType: []*descriptorpb.DescriptorProto{{
109 Name: proto.String("FieldFourEntry"),
110 Field: []*descriptorpb.FieldDescriptorProto{{
111 Name: proto.String("key"),
112 Number: proto.Int32(1),
113 Label: descriptorpb.FieldDescriptorProto_Label(protoreflect.Optional).Enum(),
114 Type: descriptorpb.FieldDescriptorProto_Type(protoreflect.StringKind).Enum(),
115 }, {
116 Name: proto.String("value"),
117 Number: proto.Int32(2),
118 Label: descriptorpb.FieldDescriptorProto_Label(protoreflect.Optional).Enum(),
119 Type: descriptorpb.FieldDescriptorProto_Type(protoreflect.MessageKind).Enum(),
120 TypeName: proto.String(".test.B"),
121 }},
122 Options: &descriptorpb.MessageOptions{
123 MapEntry: proto.Bool(true),
124 },
125 }},
126 }, {
127 Name: proto.String("C"),
128 NestedType: []*descriptorpb.DescriptorProto{{
129 Name: proto.String("A"),
130 Field: []*descriptorpb.FieldDescriptorProto{{
131 Name: proto.String("F"),
132 Number: proto.Int32(1),
133 Label: descriptorpb.FieldDescriptorProto_Label(protoreflect.Required).Enum(),
134 Type: descriptorpb.FieldDescriptorProto_Type(protoreflect.BytesKind).Enum(),
135 DefaultValue: proto.String(`dead\276\357`),
136 }},
137 }},
138 EnumType: []*descriptorpb.EnumDescriptorProto{{
139 Name: proto.String("E1"),
140 Value: []*descriptorpb.EnumValueDescriptorProto{
141 {Name: proto.String("FOO"), Number: proto.Int32(0)},
142 {Name: proto.String("BAR"), Number: proto.Int32(1)},
143 },
144 }},
145 Extension: []*descriptorpb.FieldDescriptorProto{{
146 Name: proto.String("X"),
147 Number: proto.Int32(1000),
148 Label: descriptorpb.FieldDescriptorProto_Label(protoreflect.Repeated).Enum(),
149 Type: descriptorpb.FieldDescriptorProto_Type(protoreflect.MessageKind).Enum(),
150 TypeName: proto.String(".test.C"),
151 Extendee: proto.String(".test.B"),
152 }},
153 }},
154 EnumType: []*descriptorpb.EnumDescriptorProto{{
155 Name: proto.String("E1"),
156 Options: &descriptorpb.EnumOptions{Deprecated: proto.Bool(true)},
157 Value: []*descriptorpb.EnumValueDescriptorProto{
158 {
159 Name: proto.String("FOO"),
160 Number: proto.Int32(0),
161 Options: &descriptorpb.EnumValueOptions{Deprecated: proto.Bool(true)},
162 },
163 {Name: proto.String("BAR"), Number: proto.Int32(1)},
164 },
165 ReservedName: []string{"FIZZ", "BUZZ"},
166 ReservedRange: []*descriptorpb.EnumDescriptorProto_EnumReservedRange{
167 {Start: proto.Int32(10), End: proto.Int32(19)},
168 {Start: proto.Int32(30), End: proto.Int32(30)},
169 },
170 }},
171 Extension: []*descriptorpb.FieldDescriptorProto{{
172 Name: proto.String("X"),
173 Number: proto.Int32(1000),
174 Label: descriptorpb.FieldDescriptorProto_Label(protoreflect.Repeated).Enum(),
175 Type: descriptorpb.FieldDescriptorProto_Type(protoreflect.EnumKind).Enum(),
176 Options: &descriptorpb.FieldOptions{Packed: proto.Bool(true)},
177 TypeName: proto.String(".test.E1"),
178 Extendee: proto.String(".test.B"),
179 }},
180 Service: []*descriptorpb.ServiceDescriptorProto{{
181 Name: proto.String("S"),
182 Options: &descriptorpb.ServiceOptions{Deprecated: proto.Bool(true)},
183 Method: []*descriptorpb.MethodDescriptorProto{{
184 Name: proto.String("M"),
185 InputType: proto.String(".test.A"),
186 OutputType: proto.String(".test.C.A"),
187 ClientStreaming: proto.Bool(true),
188 ServerStreaming: proto.Bool(true),
189 Options: &descriptorpb.MethodOptions{Deprecated: proto.Bool(true)},
190 }},
191 }},
192 }
193 fd1, err := protodesc.NewFile(f1, nil)
194 if err != nil {
195 t.Fatalf("protodesc.NewFile() error: %v", err)
196 }
197
198 b, err := proto.Marshal(f1)
199 if err != nil {
200 t.Fatalf("proto.Marshal() error: %v", err)
201 }
202 fd2 := filedesc.Builder{RawDescriptor: b}.Build().File
203
204 tests := []struct {
205 name string
206 desc protoreflect.FileDescriptor
207 }{
208 {"protodesc.NewFile", fd1},
209 {"filedesc.Builder.Build", fd2},
210 }
211 for _, tt := range tests {
212 tt := tt
213 t.Run(tt.name, func(t *testing.T) {
214
215 for i := 0; i < 2; i++ {
216 t.Run("Accessors", func(t *testing.T) { t.Parallel(); testFileAccessors(t, tt.desc) })
217 t.Run("Format", func(t *testing.T) { t.Parallel(); testFileFormat(t, tt.desc) })
218 }
219 })
220 }
221 }
222
223 func testFileAccessors(t *testing.T, fd protoreflect.FileDescriptor) {
224
225
226 type M = map[string]interface{}
227 want := M{
228 "Parent": nil,
229 "Index": 0,
230 "Syntax": protoreflect.Proto2,
231 "Name": protoreflect.Name("test"),
232 "FullName": protoreflect.FullName("test"),
233 "Path": "path/to/file.proto",
234 "Package": protoreflect.FullName("test"),
235 "IsPlaceholder": false,
236 "Options": &descriptorpb.FileOptions{Deprecated: proto.Bool(true)},
237 "Messages": M{
238 "Len": 3,
239 "Get:0": M{
240 "Parent": M{"FullName": protoreflect.FullName("test")},
241 "Index": 0,
242 "Syntax": protoreflect.Proto2,
243 "Name": protoreflect.Name("A"),
244 "FullName": protoreflect.FullName("test.A"),
245 "IsPlaceholder": false,
246 "IsMapEntry": false,
247 "Options": &descriptorpb.MessageOptions{
248 Deprecated: proto.Bool(true),
249 },
250 "Oneofs": M{"Len": 0},
251 "RequiredNumbers": M{"Len": 0},
252 "ExtensionRanges": M{"Len": 0},
253 "Messages": M{"Len": 0},
254 "Enums": M{"Len": 0},
255 "Extensions": M{"Len": 0},
256 },
257 "ByName:B": M{
258 "Name": protoreflect.Name("B"),
259 "Index": 1,
260 "Fields": M{
261 "Len": 6,
262 "ByJSONName:field_one": nil,
263 "ByJSONName:fieldOne": M{
264 "Name": protoreflect.Name("field_one"),
265 "Index": 0,
266 "JSONName": "fieldOne",
267 "Default": "hello, \"world!\"\n",
268 "ContainingOneof": M{"Name": protoreflect.Name("O1"), "IsPlaceholder": false},
269 "ContainingMessage": M{"FullName": protoreflect.FullName("test.B")},
270 },
271 "ByJSONName:fieldTwo": nil,
272 "ByJSONName:Field2": M{
273 "Name": protoreflect.Name("field_two"),
274 "Index": 1,
275 "HasJSONName": true,
276 "JSONName": "Field2",
277 "Default": protoreflect.EnumNumber(1),
278 "ContainingOneof": M{"Name": protoreflect.Name("O2"), "IsPlaceholder": false},
279 },
280 "ByName:fieldThree": nil,
281 "ByName:field_three": M{
282 "IsExtension": false,
283 "IsMap": false,
284 "MapKey": nil,
285 "MapValue": nil,
286 "Message": M{"FullName": protoreflect.FullName("test.C"), "IsPlaceholder": false},
287 "ContainingOneof": M{"Name": protoreflect.Name("O2"), "IsPlaceholder": false},
288 "ContainingMessage": M{"FullName": protoreflect.FullName("test.B")},
289 },
290 "ByNumber:12": nil,
291 "ByNumber:4": M{
292 "Cardinality": protoreflect.Repeated,
293 "IsExtension": false,
294 "IsList": false,
295 "IsMap": true,
296 "MapKey": M{"Kind": protoreflect.StringKind},
297 "MapValue": M{"Kind": protoreflect.MessageKind, "Message": M{"FullName": protoreflect.FullName("test.B")}},
298 "Default": nil,
299 "Message": M{"FullName": protoreflect.FullName("test.B.FieldFourEntry"), "IsPlaceholder": false},
300 },
301 "ByNumber:5": M{
302 "Cardinality": protoreflect.Repeated,
303 "Kind": protoreflect.Int32Kind,
304 "IsPacked": true,
305 "IsList": true,
306 "IsMap": false,
307 "Default": nil,
308 },
309 "ByNumber:6": M{
310 "Cardinality": protoreflect.Required,
311 "Default": []byte(nil),
312 "ContainingOneof": nil,
313 },
314 },
315 "Oneofs": M{
316 "Len": 2,
317 "ByName:O0": nil,
318 "ByName:O1": M{
319 "FullName": protoreflect.FullName("test.B.O1"),
320 "Index": 0,
321 "Options": &descriptorpb.OneofOptions{
322 UninterpretedOption: []*descriptorpb.UninterpretedOption{
323 {StringValue: []byte("option")},
324 },
325 },
326 "Fields": M{
327 "Len": 1,
328 "Get:0": M{"FullName": protoreflect.FullName("test.B.field_one")},
329 },
330 },
331 "Get:1": M{
332 "FullName": protoreflect.FullName("test.B.O2"),
333 "Index": 1,
334 "Fields": M{
335 "Len": 2,
336 "ByName:field_two": M{"Name": protoreflect.Name("field_two")},
337 "Get:1": M{"Name": protoreflect.Name("field_three")},
338 },
339 },
340 },
341 "ReservedNames": M{
342 "Len": 2,
343 "Get:0": protoreflect.Name("fizz"),
344 "Has:buzz": true,
345 "Has:noexist": false,
346 },
347 "ReservedRanges": M{
348 "Len": 2,
349 "Get:0": [2]protoreflect.FieldNumber{100, 200},
350 "Has:99": false,
351 "Has:100": true,
352 "Has:150": true,
353 "Has:199": true,
354 "Has:200": false,
355 "Has:300": true,
356 "Has:301": false,
357 },
358 "RequiredNumbers": M{
359 "Len": 1,
360 "Get:0": protoreflect.FieldNumber(6),
361 "Has:1": false,
362 "Has:6": true,
363 },
364 "ExtensionRanges": M{
365 "Len": 2,
366 "Get:0": [2]protoreflect.FieldNumber{1000, 2000},
367 "Has:999": false,
368 "Has:1000": true,
369 "Has:1500": true,
370 "Has:1999": true,
371 "Has:2000": false,
372 "Has:3000": true,
373 "Has:3001": false,
374 },
375 "ExtensionRangeOptions:0": (*descriptorpb.ExtensionRangeOptions)(nil),
376 "ExtensionRangeOptions:1": new(descriptorpb.ExtensionRangeOptions),
377 "Messages": M{
378 "Get:0": M{
379 "Fields": M{
380 "Len": 2,
381 "ByNumber:1": M{
382 "Parent": M{"FullName": protoreflect.FullName("test.B.FieldFourEntry")},
383 "Index": 0,
384 "Name": protoreflect.Name("key"),
385 "FullName": protoreflect.FullName("test.B.FieldFourEntry.key"),
386 "Number": protoreflect.FieldNumber(1),
387 "Cardinality": protoreflect.Optional,
388 "Kind": protoreflect.StringKind,
389 "Options": (*descriptorpb.FieldOptions)(nil),
390 "HasJSONName": false,
391 "JSONName": "key",
392 "IsPacked": false,
393 "IsList": false,
394 "IsMap": false,
395 "IsExtension": false,
396 "IsWeak": false,
397 "Default": "",
398 "ContainingOneof": nil,
399 "ContainingMessage": M{"FullName": protoreflect.FullName("test.B.FieldFourEntry")},
400 "Message": nil,
401 "Enum": nil,
402 },
403 "ByNumber:2": M{
404 "Parent": M{"FullName": protoreflect.FullName("test.B.FieldFourEntry")},
405 "Index": 1,
406 "Name": protoreflect.Name("value"),
407 "FullName": protoreflect.FullName("test.B.FieldFourEntry.value"),
408 "Number": protoreflect.FieldNumber(2),
409 "Cardinality": protoreflect.Optional,
410 "Kind": protoreflect.MessageKind,
411 "JSONName": "value",
412 "IsPacked": false,
413 "IsList": false,
414 "IsMap": false,
415 "IsExtension": false,
416 "IsWeak": false,
417 "Default": nil,
418 "ContainingOneof": nil,
419 "ContainingMessage": M{"FullName": protoreflect.FullName("test.B.FieldFourEntry")},
420 "Message": M{"FullName": protoreflect.FullName("test.B"), "IsPlaceholder": false},
421 "Enum": nil,
422 },
423 "ByNumber:3": nil,
424 },
425 },
426 },
427 },
428 "Get:2": M{
429 "Name": protoreflect.Name("C"),
430 "Index": 2,
431 "Messages": M{
432 "Len": 1,
433 "Get:0": M{"FullName": protoreflect.FullName("test.C.A")},
434 },
435 "Enums": M{
436 "Len": 1,
437 "Get:0": M{"FullName": protoreflect.FullName("test.C.E1")},
438 },
439 "Extensions": M{
440 "Len": 1,
441 "Get:0": M{"FullName": protoreflect.FullName("test.C.X")},
442 },
443 },
444 },
445 "Enums": M{
446 "Len": 1,
447 "Get:0": M{
448 "Name": protoreflect.Name("E1"),
449 "Options": &descriptorpb.EnumOptions{Deprecated: proto.Bool(true)},
450 "Values": M{
451 "Len": 2,
452 "ByName:Foo": nil,
453 "ByName:FOO": M{
454 "FullName": protoreflect.FullName("test.FOO"),
455 "Options": &descriptorpb.EnumValueOptions{Deprecated: proto.Bool(true)},
456 },
457 "ByNumber:2": nil,
458 "ByNumber:1": M{"FullName": protoreflect.FullName("test.BAR")},
459 },
460 "ReservedNames": M{
461 "Len": 2,
462 "Get:0": protoreflect.Name("FIZZ"),
463 "Has:BUZZ": true,
464 "Has:NOEXIST": false,
465 },
466 "ReservedRanges": M{
467 "Len": 2,
468 "Get:0": [2]protoreflect.EnumNumber{10, 19},
469 "Has:9": false,
470 "Has:10": true,
471 "Has:15": true,
472 "Has:19": true,
473 "Has:20": false,
474 "Has:30": true,
475 "Has:31": false,
476 },
477 },
478 },
479 "Extensions": M{
480 "Len": 1,
481 "ByName:X": M{
482 "Name": protoreflect.Name("X"),
483 "Number": protoreflect.FieldNumber(1000),
484 "Cardinality": protoreflect.Repeated,
485 "Kind": protoreflect.EnumKind,
486 "IsExtension": true,
487 "IsPacked": true,
488 "IsList": true,
489 "IsMap": false,
490 "MapKey": nil,
491 "MapValue": nil,
492 "ContainingOneof": nil,
493 "ContainingMessage": M{"FullName": protoreflect.FullName("test.B"), "IsPlaceholder": false},
494 "Enum": M{"FullName": protoreflect.FullName("test.E1"), "IsPlaceholder": false},
495 "Options": &descriptorpb.FieldOptions{Packed: proto.Bool(true)},
496 },
497 },
498 "Services": M{
499 "Len": 1,
500 "ByName:s": nil,
501 "ByName:S": M{
502 "Parent": M{"FullName": protoreflect.FullName("test")},
503 "Name": protoreflect.Name("S"),
504 "FullName": protoreflect.FullName("test.S"),
505 "Options": &descriptorpb.ServiceOptions{Deprecated: proto.Bool(true)},
506 "Methods": M{
507 "Len": 1,
508 "Get:0": M{
509 "Parent": M{"FullName": protoreflect.FullName("test.S")},
510 "Name": protoreflect.Name("M"),
511 "FullName": protoreflect.FullName("test.S.M"),
512 "Input": M{"FullName": protoreflect.FullName("test.A"), "IsPlaceholder": false},
513 "Output": M{"FullName": protoreflect.FullName("test.C.A"), "IsPlaceholder": false},
514 "IsStreamingClient": true,
515 "IsStreamingServer": true,
516 "Options": &descriptorpb.MethodOptions{Deprecated: proto.Bool(true)},
517 },
518 },
519 },
520 },
521 }
522 checkAccessors(t, "", reflect.ValueOf(fd), want)
523 }
524 func checkAccessors(t *testing.T, p string, rv reflect.Value, want map[string]interface{}) {
525 p0 := p
526 defer func() {
527 if ex := recover(); ex != nil {
528 t.Errorf("panic at %v: %v", p, ex)
529 }
530 }()
531
532 if rv.Interface() == nil {
533 t.Errorf("%v is nil, want non-nil", p)
534 return
535 }
536 for s, v := range want {
537
538 p = p0 + "." + s
539 var rets []reflect.Value
540 if i := strings.IndexByte(s, ':'); i >= 0 {
541
542
543 fnc := rv.MethodByName(s[:i])
544 arg := reflect.New(fnc.Type().In(0)).Elem()
545 s = s[i+len(":"):]
546 switch arg.Kind() {
547 case reflect.String:
548 arg.SetString(s)
549 case reflect.Int32, reflect.Int:
550 n, _ := strconv.ParseInt(s, 0, 64)
551 arg.SetInt(n)
552 }
553 rets = fnc.Call([]reflect.Value{arg})
554 } else {
555 rets = rv.MethodByName(s).Call(nil)
556 }
557
558
559 if len(rets) == 2 {
560 if rets[0].IsNil() && rets[1].Bool() {
561 t.Errorf("%v = (nil, true), want (nil, false)", p)
562 }
563 if !rets[0].IsNil() && !rets[1].Bool() {
564 t.Errorf("%v = (non-nil, false), want (non-nil, true)", p)
565 }
566 }
567
568
569 if want, ok := v.(map[string]interface{}); ok {
570 checkAccessors(t, p, rets[0], want)
571 continue
572 }
573
574 got := rets[0].Interface()
575 if pv, ok := got.(protoreflect.Value); ok {
576 got = pv.Interface()
577 }
578
579
580 gotMsg, gotMsgOK := got.(proto.Message)
581 wantMsg, wantMsgOK := v.(proto.Message)
582 if gotMsgOK && wantMsgOK {
583 gotNil := reflect.ValueOf(gotMsg).IsNil()
584 wantNil := reflect.ValueOf(wantMsg).IsNil()
585 switch {
586 case !gotNil && wantNil:
587 t.Errorf("%v = non-nil, want nil", p)
588 case gotNil && !wantNil:
589 t.Errorf("%v = nil, want non-nil", p)
590 case !proto.Equal(gotMsg, wantMsg):
591 t.Errorf("%v = %v, want %v", p, gotMsg, wantMsg)
592 }
593 continue
594 }
595
596 if want := v; !reflect.DeepEqual(got, want) {
597 t.Errorf("%v = %T(%v), want %T(%v)", p, got, got, want, want)
598 }
599 }
600 }
601
602 func testFileFormat(t *testing.T, fd protoreflect.FileDescriptor) {
603 const wantFileDescriptor = `FileDescriptor{
604 Syntax: proto2
605 Path: "path/to/file.proto"
606 Package: test
607 Messages: [{
608 Name: A
609 }, {
610 Name: B
611 Fields: [{
612 Name: field_one
613 Number: 1
614 Cardinality: optional
615 Kind: string
616 JSONName: "fieldOne"
617 HasPresence: true
618 HasDefault: true
619 Default: "hello, \"world!\"\n"
620 Oneof: O1
621 }, {
622 Name: field_two
623 Number: 2
624 Cardinality: optional
625 Kind: enum
626 HasJSONName: true
627 JSONName: "Field2"
628 HasPresence: true
629 HasDefault: true
630 Default: 1
631 Oneof: O2
632 Enum: test.E1
633 }, {
634 Name: field_three
635 Number: 3
636 Cardinality: optional
637 Kind: message
638 JSONName: "fieldThree"
639 HasPresence: true
640 Oneof: O2
641 Message: test.C
642 }, {
643 Name: field_four
644 Number: 4
645 Cardinality: repeated
646 Kind: message
647 HasJSONName: true
648 JSONName: "Field4"
649 IsMap: true
650 MapKey: string
651 MapValue: test.B
652 }, {
653 Name: field_five
654 Number: 5
655 Cardinality: repeated
656 Kind: int32
657 JSONName: "fieldFive"
658 IsPacked: true
659 IsList: true
660 }, {
661 Name: field_six
662 Number: 6
663 Cardinality: required
664 Kind: bytes
665 JSONName: "fieldSix"
666 HasPresence: true
667 }]
668 Oneofs: [{
669 Name: O1
670 Fields: [field_one]
671 }, {
672 Name: O2
673 Fields: [field_two, field_three]
674 }]
675 ReservedNames: [fizz, buzz]
676 ReservedRanges: [100:200, 300]
677 RequiredNumbers: [6]
678 ExtensionRanges: [1000:2000, 3000]
679 Messages: [{
680 Name: FieldFourEntry
681 IsMapEntry: true
682 Fields: [{
683 Name: key
684 Number: 1
685 Cardinality: optional
686 Kind: string
687 JSONName: "key"
688 HasPresence: true
689 }, {
690 Name: value
691 Number: 2
692 Cardinality: optional
693 Kind: message
694 JSONName: "value"
695 HasPresence: true
696 Message: test.B
697 }]
698 }]
699 }, {
700 Name: C
701 Messages: [{
702 Name: A
703 Fields: [{
704 Name: F
705 Number: 1
706 Cardinality: required
707 Kind: bytes
708 JSONName: "F"
709 HasPresence: true
710 HasDefault: true
711 Default: "dead\xbe\xef"
712 }]
713 RequiredNumbers: [1]
714 }]
715 Enums: [{
716 Name: E1
717 Values: [
718 {Name: FOO}
719 {Name: BAR, Number: 1}
720 ]
721 }]
722 Extensions: [{
723 Name: X
724 Number: 1000
725 Cardinality: repeated
726 Kind: message
727 JSONName: "[test.C.X]"
728 IsExtension: true
729 IsList: true
730 Extendee: test.B
731 Message: test.C
732 }]
733 }]
734 Enums: [{
735 Name: E1
736 Values: [
737 {Name: FOO}
738 {Name: BAR, Number: 1}
739 ]
740 ReservedNames: [FIZZ, BUZZ]
741 ReservedRanges: [10:20, 30]
742 }]
743 Extensions: [{
744 Name: X
745 Number: 1000
746 Cardinality: repeated
747 Kind: enum
748 JSONName: "[test.X]"
749 IsExtension: true
750 IsPacked: true
751 IsList: true
752 Extendee: test.B
753 Enum: test.E1
754 }]
755 Services: [{
756 Name: S
757 Methods: [{
758 Name: M
759 Input: test.A
760 Output: test.C.A
761 IsStreamingClient: true
762 IsStreamingServer: true
763 }]
764 }]
765 }`
766
767 const wantEnums = `Enums{{
768 Name: E1
769 Values: [
770 {Name: FOO}
771 {Name: BAR, Number: 1}
772 ]
773 ReservedNames: [FIZZ, BUZZ]
774 ReservedRanges: [10:20, 30]
775 }}`
776
777 const wantExtensions = `Extensions{{
778 Name: X
779 Number: 1000
780 Cardinality: repeated
781 Kind: enum
782 JSONName: "[test.X]"
783 IsExtension: true
784 IsPacked: true
785 IsList: true
786 Extendee: test.B
787 Enum: test.E1
788 }}`
789
790 const wantImports = `FileImports{}`
791
792 const wantReservedNames = "Names{fizz, buzz}"
793
794 const wantReservedRanges = "FieldRanges{100:200, 300}"
795
796 const wantServices = `Services{{
797 Name: S
798 Methods: [{
799 Name: M
800 Input: test.A
801 Output: test.C.A
802 IsStreamingClient: true
803 IsStreamingServer: true
804 }]
805 }}`
806
807 tests := []struct {
808 path string
809 fmt string
810 want string
811 val interface{}
812 }{
813 {"fd", "%v", compactMultiFormat(wantFileDescriptor), fd},
814 {"fd", "%+v", wantFileDescriptor, fd},
815 {"fd.Enums()", "%v", compactMultiFormat(wantEnums), fd.Enums()},
816 {"fd.Enums()", "%+v", wantEnums, fd.Enums()},
817 {"fd.Extensions()", "%v", compactMultiFormat(wantExtensions), fd.Extensions()},
818 {"fd.Extensions()", "%+v", wantExtensions, fd.Extensions()},
819 {"fd.Imports()", "%v", compactMultiFormat(wantImports), fd.Imports()},
820 {"fd.Imports()", "%+v", wantImports, fd.Imports()},
821 {"fd.Messages(B).ReservedNames()", "%v", compactMultiFormat(wantReservedNames), fd.Messages().ByName("B").ReservedNames()},
822 {"fd.Messages(B).ReservedNames()", "%+v", wantReservedNames, fd.Messages().ByName("B").ReservedNames()},
823 {"fd.Messages(B).ReservedRanges()", "%v", compactMultiFormat(wantReservedRanges), fd.Messages().ByName("B").ReservedRanges()},
824 {"fd.Messages(B).ReservedRanges()", "%+v", wantReservedRanges, fd.Messages().ByName("B").ReservedRanges()},
825 {"fd.Services()", "%v", compactMultiFormat(wantServices), fd.Services()},
826 {"fd.Services()", "%+v", wantServices, fd.Services()},
827 }
828 for _, tt := range tests {
829 got := fmt.Sprintf(tt.fmt, tt.val)
830 if diff := cmp.Diff(got, tt.want); diff != "" {
831 t.Errorf("fmt.Sprintf(%q, %s) mismatch (-got +want):\n%s", tt.fmt, tt.path, diff)
832 }
833 }
834 }
835
836
837 func compactMultiFormat(s string) string {
838 var b []byte
839 for _, s := range strings.Split(s, "\n") {
840 s = strings.TrimSpace(s)
841 s = regexp.MustCompile(": +").ReplaceAllString(s, ": ")
842 prevWord := len(b) > 0 && b[len(b)-1] != '[' && b[len(b)-1] != '{'
843 nextWord := len(s) > 0 && s[0] != ']' && s[0] != '}'
844 if prevWord && nextWord {
845 b = append(b, ", "...)
846 }
847 b = append(b, s...)
848 }
849 return string(b)
850 }
851
View as plain text