1
2
3
4
5 package prototext_test
6
7 import (
8 "math"
9 "strings"
10 "testing"
11
12 "google.golang.org/protobuf/encoding/prototext"
13 "google.golang.org/protobuf/internal/flags"
14 "google.golang.org/protobuf/proto"
15 "google.golang.org/protobuf/reflect/protoregistry"
16
17 testpb "google.golang.org/protobuf/internal/testprotos/test"
18 weakpb "google.golang.org/protobuf/internal/testprotos/test/weak1"
19 pb2 "google.golang.org/protobuf/internal/testprotos/textpb2"
20 pb3 "google.golang.org/protobuf/internal/testprotos/textpb3"
21 "google.golang.org/protobuf/types/known/anypb"
22 )
23
24 func TestUnmarshal(t *testing.T) {
25 tests := []struct {
26 desc string
27 umo prototext.UnmarshalOptions
28 inputMessage proto.Message
29 inputText string
30 wantMessage proto.Message
31 wantErr string
32 skip bool
33 }{{
34 desc: "proto2 empty message",
35 inputMessage: &pb2.Scalars{},
36 wantMessage: &pb2.Scalars{},
37 }, {
38 desc: "proto2 optional scalars set to zero values",
39 inputMessage: &pb2.Scalars{},
40 inputText: `opt_bool: false
41 opt_int32: 0
42 opt_int64: 0
43 opt_uint32: 0
44 opt_uint64: 0
45 opt_sint32: 0
46 opt_sint64: 0
47 opt_fixed32: 0
48 opt_fixed64: 0
49 opt_sfixed32: 0
50 opt_sfixed64: 0
51 opt_float: 0
52 opt_double: 0
53 opt_bytes: ""
54 opt_string: ""
55 `,
56 wantMessage: &pb2.Scalars{
57 OptBool: proto.Bool(false),
58 OptInt32: proto.Int32(0),
59 OptInt64: proto.Int64(0),
60 OptUint32: proto.Uint32(0),
61 OptUint64: proto.Uint64(0),
62 OptSint32: proto.Int32(0),
63 OptSint64: proto.Int64(0),
64 OptFixed32: proto.Uint32(0),
65 OptFixed64: proto.Uint64(0),
66 OptSfixed32: proto.Int32(0),
67 OptSfixed64: proto.Int64(0),
68 OptFloat: proto.Float32(0),
69 OptDouble: proto.Float64(0),
70 OptBytes: []byte{},
71 OptString: proto.String(""),
72 },
73 }, {
74 desc: "proto3 scalars set to zero values",
75 inputMessage: &pb3.Scalars{},
76 inputText: `s_bool: false
77 s_int32: 0
78 s_int64: 0
79 s_uint32: 0
80 s_uint64: 0
81 s_sint32: 0
82 s_sint64: 0
83 s_fixed32: 0
84 s_fixed64: 0
85 s_sfixed32: 0
86 s_sfixed64: 0
87 s_float: 0
88 s_double: 0
89 s_bytes: ""
90 s_string: ""
91 `,
92 wantMessage: &pb3.Scalars{},
93 }, {
94 desc: "proto3 optional set to zero values",
95 inputMessage: &pb3.Proto3Optional{},
96 inputText: `opt_bool: false
97 opt_int32: 0
98 opt_int64: 0
99 opt_uint32: 0
100 opt_uint64: 0
101 opt_float: 0
102 opt_double: 0
103 opt_string: ""
104 opt_bytes: ""
105 opt_enum: ZERO
106 opt_message: {}
107 `,
108 wantMessage: &pb3.Proto3Optional{
109 OptBool: proto.Bool(false),
110 OptInt32: proto.Int32(0),
111 OptInt64: proto.Int64(0),
112 OptUint32: proto.Uint32(0),
113 OptUint64: proto.Uint64(0),
114 OptFloat: proto.Float32(0),
115 OptDouble: proto.Float64(0),
116 OptString: proto.String(""),
117 OptBytes: []byte{},
118 OptEnum: pb3.Enum_ZERO.Enum(),
119 OptMessage: &pb3.Nested{},
120 },
121 }, {
122 desc: "proto2 optional scalars",
123 inputMessage: &pb2.Scalars{},
124 inputText: `opt_bool: true
125 opt_int32: 255
126 opt_int64: 3735928559
127 opt_uint32: 0xff
128 opt_uint64: 0xdeadbeef
129 opt_sint32: -1001
130 opt_sint64: - 0xffff
131 opt_fixed64: 64
132 opt_sfixed32: - 32
133 opt_float: 1.234
134 opt_double: 1.23e+100
135 opt_bytes: "\xe8\xb0\xb7\xe6\xad\x8c"
136 opt_string: "谷歌"
137 `,
138 wantMessage: &pb2.Scalars{
139 OptBool: proto.Bool(true),
140 OptInt32: proto.Int32(0xff),
141 OptInt64: proto.Int64(0xdeadbeef),
142 OptUint32: proto.Uint32(0xff),
143 OptUint64: proto.Uint64(0xdeadbeef),
144 OptSint32: proto.Int32(-1001),
145 OptSint64: proto.Int64(-0xffff),
146 OptFixed64: proto.Uint64(64),
147 OptSfixed32: proto.Int32(-32),
148 OptFloat: proto.Float32(1.234),
149 OptDouble: proto.Float64(1.23e100),
150 OptBytes: []byte("\xe8\xb0\xb7\xe6\xad\x8c"),
151 OptString: proto.String("谷歌"),
152 },
153 }, {
154 desc: "case sensitive",
155 inputMessage: &pb3.Scalars{},
156 inputText: `S_BOOL: true`,
157 wantErr: "unknown field: S_BOOL",
158 }, {
159 desc: "proto3 scalars",
160 inputMessage: &pb3.Scalars{},
161 inputText: `s_bool: true
162 s_int32: 255
163 s_int64: 3735928559
164 s_uint32: 0xff
165 s_uint64: 0xdeadbeef
166 s_sint32: -1001
167 s_sint64: - #
168 0xffff
169 s_fixed64: 64
170 s_sfixed32: -32
171 s_float: 1.234
172 s_double: 1.23e+100
173 s_bytes: "\xe8\xb0\xb7\xe6\xad\x8c"
174 s_string: "谷歌"
175 `,
176 wantMessage: &pb3.Scalars{
177 SBool: true,
178 SInt32: 0xff,
179 SInt64: 0xdeadbeef,
180 SUint32: 0xff,
181 SUint64: 0xdeadbeef,
182 SSint32: -1001,
183 SSint64: -0xffff,
184 SFixed64: 64,
185 SSfixed32: -32,
186 SFloat: 1.234,
187 SDouble: 1.23e100,
188 SBytes: []byte("\xe8\xb0\xb7\xe6\xad\x8c"),
189 SString: "谷歌",
190 },
191 }, {
192 desc: "proto2 string with invalid UTF-8",
193 inputMessage: &pb2.Scalars{},
194 inputText: `opt_string: "abc\xff"`,
195 wantMessage: &pb2.Scalars{
196 OptString: proto.String("abc\xff"),
197 },
198 }, {
199 desc: "proto3 string with invalid UTF-8",
200 inputMessage: &pb3.Scalars{},
201 inputText: `s_string: "abc\xff"`,
202 wantErr: "(line 1:11): contains invalid UTF-8",
203 }, {
204 desc: "proto2 message contains unknown field",
205 inputMessage: &pb2.Scalars{},
206 inputText: "unknown_field: 123",
207 wantErr: "unknown field",
208 }, {
209 desc: "proto3 message contains unknown field",
210 inputMessage: &pb3.Scalars{},
211 inputText: "unknown_field: 456",
212 wantErr: "unknown field",
213 }, {
214 desc: "proto2 message contains discarded unknown field",
215 umo: prototext.UnmarshalOptions{DiscardUnknown: true},
216 inputMessage: &pb2.Scalars{},
217 inputText: `unknown_field:123 1000:"hello"`,
218 }, {
219 desc: "proto3 message contains discarded unknown field",
220 umo: prototext.UnmarshalOptions{DiscardUnknown: true},
221 inputMessage: &pb3.Scalars{},
222 inputText: `unknown_field:456 1000:"goodbye"`,
223 }, {
224 desc: "proto2 message cannot parse field number",
225 umo: prototext.UnmarshalOptions{DiscardUnknown: true},
226 inputMessage: &pb2.Scalars{},
227 inputText: `13:"hello"`,
228 wantErr: "cannot specify field by number",
229 }, {
230 desc: "unknown list field",
231 umo: prototext.UnmarshalOptions{DiscardUnknown: true},
232 inputMessage: &pb2.Scalars{},
233 inputText: `unknown_field: { strings: [ "" ] }`,
234 }, {
235 desc: "unknown list of list field",
236 umo: prototext.UnmarshalOptions{DiscardUnknown: true},
237 inputMessage: &pb2.Scalars{},
238 inputText: `unknown_field: { strings: [ [ ] ] }`,
239 wantErr: `(line 1:29): invalid scalar value: [`,
240 }, {
241 desc: "unknown list of message field",
242 umo: prototext.UnmarshalOptions{DiscardUnknown: true},
243 inputMessage: &pb2.Scalars{},
244 inputText: `unknown_field: [ { a: "b" }, { c: "d" } ]`,
245 }, {
246 desc: "proto3 message cannot parse field number",
247 umo: prototext.UnmarshalOptions{DiscardUnknown: true},
248 inputMessage: &pb3.Scalars{},
249 inputText: `13:"goodbye"`,
250 wantErr: "cannot specify field by number",
251 }, {
252 desc: "proto2 numeric key field",
253 inputMessage: &pb2.Scalars{},
254 inputText: "1: true",
255 wantErr: "cannot specify field by number",
256 }, {
257 desc: "proto3 numeric key field",
258 inputMessage: &pb3.Scalars{},
259 inputText: "1: true",
260 wantErr: "cannot specify field by number",
261 }, {
262 desc: "invalid bool value",
263 inputMessage: &pb3.Scalars{},
264 inputText: "s_bool: 123",
265 wantErr: "invalid value for bool",
266 }, {
267 desc: "invalid int32 value",
268 inputMessage: &pb3.Scalars{},
269 inputText: "s_int32: not_a_num",
270 wantErr: "invalid value for int32",
271 }, {
272 desc: "invalid int64 value",
273 inputMessage: &pb3.Scalars{},
274 inputText: "s_int64: 'not a num either'",
275 wantErr: "invalid value for int64",
276 }, {
277 desc: "invalid uint32 value",
278 inputMessage: &pb3.Scalars{},
279 inputText: "s_fixed32: -42",
280 wantErr: "invalid value for fixed32",
281 }, {
282 desc: "invalid uint64 value",
283 inputMessage: &pb3.Scalars{},
284 inputText: "s_uint64: -47",
285 wantErr: "invalid value for uint64",
286 }, {
287 desc: "invalid sint32 value",
288 inputMessage: &pb3.Scalars{},
289 inputText: "s_sint32: '42'",
290 wantErr: "invalid value for sint32",
291 }, {
292 desc: "invalid sint64 value",
293 inputMessage: &pb3.Scalars{},
294 inputText: "s_sint64: '-47'",
295 wantErr: "invalid value for sint64",
296 }, {
297 desc: "invalid fixed32 value",
298 inputMessage: &pb3.Scalars{},
299 inputText: "s_fixed32: -42",
300 wantErr: "invalid value for fixed32",
301 }, {
302 desc: "invalid fixed64 value",
303 inputMessage: &pb3.Scalars{},
304 inputText: "s_fixed64: -42",
305 wantErr: "invalid value for fixed64",
306 }, {
307 desc: "invalid sfixed32 value",
308 inputMessage: &pb3.Scalars{},
309 inputText: "s_sfixed32: 'not valid'",
310 wantErr: "invalid value for sfixed32",
311 }, {
312 desc: "invalid sfixed64 value",
313 inputMessage: &pb3.Scalars{},
314 inputText: "s_sfixed64: bad",
315 wantErr: "invalid value for sfixed64",
316 }, {
317 desc: "incomplete number value",
318 inputMessage: &pb3.Scalars{},
319 inputText: `s_int32: - `,
320 wantErr: "(line 1:10): invalid scalar value: -",
321 }, {
322 desc: "conformance: FloatFieldMaxValue",
323 inputMessage: &pb2.Scalars{},
324 inputText: `opt_float: 3.4028235e+38`,
325 wantMessage: &pb2.Scalars{
326 OptFloat: proto.Float32(3.40282347e+38),
327 },
328 }, {
329 desc: "conformance: FloatFieldLargerThanUint64",
330 inputMessage: &pb2.Scalars{},
331 inputText: `opt_float: 18446744073709551616`,
332 wantMessage: &pb2.Scalars{
333 OptFloat: proto.Float32(1.84467441e+19),
334 },
335 }, {
336 desc: "conformance: FloatFieldTooLarge",
337 inputMessage: &pb2.Scalars{},
338 inputText: `opt_float: 3.4028235e+39`,
339 wantMessage: &pb2.Scalars{
340 OptFloat: proto.Float32(float32(math.Inf(1))),
341 },
342 }, {
343 desc: "invalid string value",
344 inputMessage: &pb3.Scalars{},
345 inputText: "s_string: invalid_string",
346 wantErr: "invalid value for string type",
347 }, {
348 desc: "proto2 bytes set to empty string",
349 inputMessage: &pb2.Scalars{},
350 inputText: "opt_bytes: ''",
351 wantMessage: &pb2.Scalars{
352 OptBytes: []byte(""),
353 },
354 }, {
355 desc: "proto3 bytes set to empty string",
356 inputMessage: &pb3.Scalars{},
357 inputText: "s_bytes: ''",
358 wantMessage: &pb3.Scalars{},
359 }, {
360 desc: "proto2 duplicate singular field",
361 inputMessage: &pb2.Scalars{},
362 inputText: `
363 opt_bool: true
364 opt_bool: false
365 `,
366 wantErr: `(line 3:1): non-repeated field "opt_bool" is repeated`,
367 }, {
368 desc: "proto2 more duplicate singular field",
369 inputMessage: &pb2.Scalars{},
370 inputText: `
371 opt_bool: true
372 opt_string: "hello"
373 opt_bool: false
374 `,
375 wantErr: `(line 4:1): non-repeated field "opt_bool" is repeated`,
376 }, {
377 desc: "proto2 invalid singular field",
378 inputMessage: &pb2.Scalars{},
379 inputText: `
380 opt_bool: [true, false]
381 `,
382 wantErr: "(line 2:11): unexpected token: [",
383 }, {
384 desc: "proto3 duplicate singular field",
385 inputMessage: &pb3.Scalars{},
386 inputText: `
387 s_bool: false
388 s_bool: true
389 `,
390 wantErr: `non-repeated field "s_bool" is repeated`,
391 }, {
392 desc: "proto3 more duplicate singular field",
393 inputMessage: &pb3.Scalars{},
394 inputText: `
395 s_bool: false
396 s_string: ""
397 s_bool: true
398 `,
399 wantErr: `non-repeated field "s_bool" is repeated`,
400 }, {
401 desc: "proto2 enum",
402 inputMessage: &pb2.Enums{},
403 inputText: `
404 opt_enum: ONE
405 opt_nested_enum: UNO
406 `,
407 wantMessage: &pb2.Enums{
408 OptEnum: pb2.Enum_ONE.Enum(),
409 OptNestedEnum: pb2.Enums_UNO.Enum(),
410 },
411 }, {
412 desc: "proto2 enum set to numeric values",
413 inputMessage: &pb2.Enums{},
414 inputText: `
415 opt_enum: 2
416 opt_nested_enum: 2
417 `,
418 wantMessage: &pb2.Enums{
419 OptEnum: pb2.Enum_TWO.Enum(),
420 OptNestedEnum: pb2.Enums_DOS.Enum(),
421 },
422 }, {
423 desc: "proto2 enum set to unnamed numeric values",
424 inputMessage: &pb2.Enums{},
425 inputText: `
426 opt_enum: 101
427 opt_nested_enum: -101
428 `,
429 wantMessage: &pb2.Enums{
430 OptEnum: pb2.Enum(101).Enum(),
431 OptNestedEnum: pb2.Enums_NestedEnum(-101).Enum(),
432 },
433 }, {
434 desc: "proto2 enum set to invalid named",
435 inputMessage: &pb2.Enums{},
436 inputText: `
437 opt_enum: UNNAMED
438 opt_nested_enum: UNNAMED_TOO
439 `,
440 wantErr: "invalid value for enum type: UNNAMED",
441 }, {
442 desc: "proto3 enum name value",
443 inputMessage: &pb3.Enums{},
444 inputText: `
445 s_enum: ONE
446 s_nested_enum: DIEZ
447 `,
448 wantMessage: &pb3.Enums{
449 SEnum: pb3.Enum_ONE,
450 SNestedEnum: pb3.Enums_DIEZ,
451 },
452 }, {
453 desc: "proto3 enum numeric value",
454 inputMessage: &pb3.Enums{},
455 inputText: `
456 s_enum: 2
457 s_nested_enum: 2
458 `,
459 wantMessage: &pb3.Enums{
460 SEnum: pb3.Enum_TWO,
461 SNestedEnum: pb3.Enums_DOS,
462 },
463 }, {
464 desc: "proto3 enum unnamed numeric value",
465 inputMessage: &pb3.Enums{},
466 inputText: `
467 s_enum: 0x7fffffff
468 s_nested_enum: -0x80000000
469 `,
470 wantMessage: &pb3.Enums{
471 SEnum: 0x7fffffff,
472 SNestedEnum: -0x80000000,
473 },
474 }, {
475 desc: "proto2 nested empty messages",
476 inputMessage: &pb2.Nests{},
477 inputText: `
478 opt_nested: {}
479 OptGroup: {}
480 `,
481 wantMessage: &pb2.Nests{
482 OptNested: &pb2.Nested{},
483 Optgroup: &pb2.Nests_OptGroup{},
484 },
485 }, {
486 desc: "message fields with no field separator",
487 inputMessage: &pb2.Nests{},
488 inputText: `
489 opt_nested {}
490 OptGroup {}
491 `,
492 wantMessage: &pb2.Nests{
493 OptNested: &pb2.Nested{},
494 Optgroup: &pb2.Nests_OptGroup{},
495 },
496 }, {
497 desc: "group field name",
498 inputMessage: &pb2.Nests{},
499 inputText: `optgroup: {}`,
500 wantErr: "unknown field: optgroup",
501 }, {
502 desc: "proto2 nested messages",
503 inputMessage: &pb2.Nests{},
504 inputText: `
505 opt_nested: {
506 opt_string: "nested message"
507 opt_nested: {
508 opt_string: "another nested message"
509 }
510 }
511 `,
512 wantMessage: &pb2.Nests{
513 OptNested: &pb2.Nested{
514 OptString: proto.String("nested message"),
515 OptNested: &pb2.Nested{
516 OptString: proto.String("another nested message"),
517 },
518 },
519 },
520 }, {
521 desc: "proto3 nested empty message",
522 inputMessage: &pb3.Nests{},
523 inputText: "s_nested: {}",
524 wantMessage: &pb3.Nests{
525 SNested: &pb3.Nested{},
526 },
527 }, {
528 desc: "proto3 nested message",
529 inputMessage: &pb3.Nests{},
530 inputText: `
531 s_nested: {
532 s_string: "nested message"
533 s_nested: {
534 s_string: "another nested message"
535 }
536 }
537 `,
538 wantMessage: &pb3.Nests{
539 SNested: &pb3.Nested{
540 SString: "nested message",
541 SNested: &pb3.Nested{
542 SString: "another nested message",
543 },
544 },
545 },
546 }, {
547 desc: "proto3 nested message contains invalid UTF-8",
548 inputMessage: &pb3.Nests{},
549 inputText: `s_nested: {
550 s_string: "abc\xff"
551 }
552 `,
553 wantErr: "contains invalid UTF-8",
554 }, {
555 desc: "oneof set to empty string",
556 inputMessage: &pb3.Oneofs{},
557 inputText: "oneof_string: ''",
558 wantMessage: &pb3.Oneofs{
559 Union: &pb3.Oneofs_OneofString{},
560 },
561 }, {
562 desc: "oneof set to string",
563 inputMessage: &pb3.Oneofs{},
564 inputText: "oneof_string: 'hello'",
565 wantMessage: &pb3.Oneofs{
566 Union: &pb3.Oneofs_OneofString{
567 OneofString: "hello",
568 },
569 },
570 }, {
571 desc: "oneof set to enum",
572 inputMessage: &pb3.Oneofs{},
573 inputText: "oneof_enum: TEN",
574 wantMessage: &pb3.Oneofs{
575 Union: &pb3.Oneofs_OneofEnum{
576 OneofEnum: pb3.Enum_TEN,
577 },
578 },
579 }, {
580 desc: "oneof set to empty message",
581 inputMessage: &pb3.Oneofs{},
582 inputText: "oneof_nested: {}",
583 wantMessage: &pb3.Oneofs{
584 Union: &pb3.Oneofs_OneofNested{
585 OneofNested: &pb3.Nested{},
586 },
587 },
588 }, {
589 desc: "oneof set to message",
590 inputMessage: &pb3.Oneofs{},
591 inputText: `
592 oneof_nested: {
593 s_string: "nested message"
594 }
595 `,
596 wantMessage: &pb3.Oneofs{
597 Union: &pb3.Oneofs_OneofNested{
598 OneofNested: &pb3.Nested{
599 SString: "nested message",
600 },
601 },
602 },
603 }, {
604 desc: "oneof set to more than one field",
605 inputMessage: &pb3.Oneofs{},
606 inputText: `
607 oneof_enum: ZERO
608 oneof_string: "hello"
609 `,
610 wantErr: `error parsing "oneof_string", oneof pb3.Oneofs.union is already set`,
611 }, {
612 desc: "repeated scalar using same field name",
613 inputMessage: &pb2.Repeats{},
614 inputText: `
615 rpt_string: "a"
616 rpt_string: "b"
617 rpt_int32: 0xff
618 rpt_float: 1.23
619 rpt_bytes: "bytes"
620 `,
621 wantMessage: &pb2.Repeats{
622 RptString: []string{"a", "b"},
623 RptInt32: []int32{0xff},
624 RptFloat: []float32{1.23},
625 RptBytes: [][]byte{[]byte("bytes")},
626 },
627 }, {
628 desc: "repeated using mix of [] and repeated field name",
629 inputMessage: &pb2.Repeats{},
630 inputText: `
631 rpt_string: "a"
632 rpt_bool: true
633 rpt_string: ["x", "y"]
634 rpt_bool: [ false, true ]
635 rpt_string: "b"
636 `,
637 wantMessage: &pb2.Repeats{
638 RptString: []string{"a", "x", "y", "b"},
639 RptBool: []bool{true, false, true},
640 },
641 }, {
642 desc: "repeated proto2 contains invalid UTF-8",
643 inputMessage: &pb2.Repeats{},
644 inputText: `rpt_string: "abc\xff"`,
645 wantMessage: &pb2.Repeats{
646 RptString: []string{"abc\xff"},
647 },
648 }, {
649 desc: "repeated proto3 contains invalid UTF-8",
650 inputMessage: &pb3.Repeats{},
651 inputText: `rpt_string: "abc\xff"`,
652 wantErr: "contains invalid UTF-8",
653 }, {
654 desc: "repeated enums",
655 inputMessage: &pb2.Enums{},
656 inputText: `
657 rpt_enum: TEN
658 rpt_enum: 1
659 rpt_nested_enum: [DOS, 2]
660 rpt_enum: 42
661 rpt_nested_enum: -47
662 `,
663 wantMessage: &pb2.Enums{
664 RptEnum: []pb2.Enum{pb2.Enum_TEN, pb2.Enum_ONE, 42},
665 RptNestedEnum: []pb2.Enums_NestedEnum{pb2.Enums_DOS, pb2.Enums_DOS, -47},
666 },
667 }, {
668 desc: "repeated nested messages",
669 inputMessage: &pb2.Nests{},
670 inputText: `
671 rpt_nested: {
672 opt_string: "repeat nested one"
673 }
674 rpt_nested: {
675 opt_string: "repeat nested two"
676 opt_nested: {
677 opt_string: "inside repeat nested two"
678 }
679 }
680 rpt_nested: {}
681 `,
682 wantMessage: &pb2.Nests{
683 RptNested: []*pb2.Nested{
684 {
685 OptString: proto.String("repeat nested one"),
686 },
687 {
688 OptString: proto.String("repeat nested two"),
689 OptNested: &pb2.Nested{
690 OptString: proto.String("inside repeat nested two"),
691 },
692 },
693 {},
694 },
695 },
696 }, {
697 desc: "repeated group fields",
698 inputMessage: &pb2.Nests{},
699 inputText: `
700 RptGroup: {
701 rpt_string: "hello"
702 rpt_string: "world"
703 }
704 RptGroup: {}
705 `,
706 wantMessage: &pb2.Nests{
707 Rptgroup: []*pb2.Nests_RptGroup{
708 {
709 RptString: []string{"hello", "world"},
710 },
711 {},
712 },
713 },
714 }, {
715 desc: "repeated message fields without field separator",
716 inputMessage: &pb2.Nests{},
717 inputText: `
718 rpt_nested {
719 opt_string: "repeat nested one"
720 }
721 rpt_nested: [
722 {
723 opt_string: "repeat nested two"
724 },
725 {}
726 ]
727 `,
728 wantMessage: &pb2.Nests{
729 RptNested: []*pb2.Nested{
730 {
731 OptString: proto.String("repeat nested one"),
732 },
733 {
734 OptString: proto.String("repeat nested two"),
735 },
736 {},
737 },
738 },
739 }, {
740 desc: "bools",
741 inputMessage: &pb2.Repeats{},
742 inputText: `
743 rpt_bool: [ True, true, t, 1, False, false, f, 0 ]
744 `,
745 wantMessage: &pb2.Repeats{
746 RptBool: []bool{true, true, true, true, false, false, false, false},
747 },
748 }, {
749 desc: "special floats and doubles",
750 inputMessage: &pb2.Repeats{},
751 inputText: `
752 rpt_float: [ inf, Inf, infinity, InFiniTy, -inf, -inF, -infinitY, -InfinitY, nan, NaN, Nan ],
753 rpt_double: [ inf, Inf, infinity, InFiniTy, -inf, -inF, -infinitY, -InfinitY, nan, NaN, Nan ],
754 `,
755 wantMessage: &pb2.Repeats{
756 RptFloat: []float32{
757 float32(math.Inf(1)),
758 float32(math.Inf(1)),
759 float32(math.Inf(1)),
760 float32(math.Inf(1)),
761 float32(math.Inf(-1)),
762 float32(math.Inf(-1)),
763 float32(math.Inf(-1)),
764 float32(math.Inf(-1)),
765 float32(math.NaN()),
766 float32(math.NaN()),
767 float32(math.NaN()),
768 },
769 RptDouble: []float64{
770 math.Inf(1),
771 math.Inf(1),
772 math.Inf(1),
773 math.Inf(1),
774 math.Inf(-1),
775 math.Inf(-1),
776 math.Inf(-1),
777 math.Inf(-1),
778 math.NaN(),
779 math.NaN(),
780 math.NaN(),
781 },
782 },
783 }, {
784 desc: "map fields 1",
785 inputMessage: &pb3.Maps{},
786 inputText: `
787 int32_to_str: {
788 key: -101
789 value: "-101"
790 }
791 int32_to_str {
792 key: 0
793 value: "zero"
794 }
795 bool_to_uint32: {
796 key: false
797 value: 101
798 }
799 int32_to_str: {
800 key: 255
801 value: "0xff"
802 }
803 bool_to_uint32 {
804 key: true
805 value: 42
806 }
807 `,
808 wantMessage: &pb3.Maps{
809 Int32ToStr: map[int32]string{
810 -101: "-101",
811 0xff: "0xff",
812 0: "zero",
813 },
814 BoolToUint32: map[bool]uint32{
815 true: 42,
816 false: 101,
817 },
818 },
819 }, {
820 desc: "map fields 2",
821 inputMessage: &pb3.Maps{},
822 inputText: `
823 uint64_to_enum: {
824 key: 1
825 value: ONE
826 }
827 uint64_to_enum: {
828 key: 2
829 value: 2
830 }
831 uint64_to_enum: {
832 key: 10
833 value: 101
834 }
835 `,
836 wantMessage: &pb3.Maps{
837 Uint64ToEnum: map[uint64]pb3.Enum{
838 1: pb3.Enum_ONE,
839 2: pb3.Enum_TWO,
840 10: 101,
841 },
842 },
843 }, {
844 desc: "map fields 3",
845 inputMessage: &pb3.Maps{},
846 inputText: `
847 str_to_nested: {
848 key: "nested_one"
849 value {
850 s_string: "nested in a map"
851 }
852 }
853 `,
854 wantMessage: &pb3.Maps{
855 StrToNested: map[string]*pb3.Nested{
856 "nested_one": &pb3.Nested{
857 SString: "nested in a map",
858 },
859 },
860 },
861 }, {
862 desc: "map fields 4",
863 inputMessage: &pb3.Maps{},
864 inputText: `
865 str_to_oneofs: {
866 key: "nested"
867 value: {
868 oneof_nested: {
869 s_string: "nested oneof in map field value"
870 }
871 }
872 }
873 str_to_oneofs: {
874 key: "string"
875 value: {
876 oneof_string: "hello"
877 }
878 }
879 `,
880 wantMessage: &pb3.Maps{
881 StrToOneofs: map[string]*pb3.Oneofs{
882 "string": &pb3.Oneofs{
883 Union: &pb3.Oneofs_OneofString{
884 OneofString: "hello",
885 },
886 },
887 "nested": &pb3.Oneofs{
888 Union: &pb3.Oneofs_OneofNested{
889 OneofNested: &pb3.Nested{
890 SString: "nested oneof in map field value",
891 },
892 },
893 },
894 },
895 },
896 }, {
897 desc: "map contains duplicate keys",
898 inputMessage: &pb3.Maps{},
899 inputText: `
900 int32_to_str: {
901 key: 0
902 value: "cero"
903 }
904 int32_to_str: {
905 key: 0
906 value: "zero"
907 }
908 `,
909 wantMessage: &pb3.Maps{
910 Int32ToStr: map[int32]string{
911 0: "zero",
912 },
913 },
914 }, {
915 desc: "map contains duplicate key fields",
916 inputMessage: &pb3.Maps{},
917 inputText: `
918 int32_to_str: {
919 key: 0
920 key: 1
921 value: "cero"
922 }
923 `,
924 wantErr: `map entry "key" cannot be repeated`,
925 }, {
926 desc: "map contains duplicate value fields",
927 inputMessage: &pb3.Maps{},
928 inputText: `
929 int32_to_str: {
930 key: 1
931 value: "cero"
932 value: "uno"
933 }
934 `,
935 wantErr: `map entry "value" cannot be repeated`,
936 }, {
937 desc: "map contains missing key",
938 inputMessage: &pb3.Maps{},
939 inputText: `
940 int32_to_str: {
941 value: "zero"
942 }
943 bool_to_uint32: {
944 value: 47
945 }
946 str_to_nested: {
947 value: {}
948 }
949 `,
950 wantMessage: &pb3.Maps{
951 Int32ToStr: map[int32]string{
952 0: "zero",
953 },
954 BoolToUint32: map[bool]uint32{
955 false: 47,
956 },
957 StrToNested: map[string]*pb3.Nested{
958 "": {},
959 },
960 },
961 }, {
962 desc: "map contains missing value",
963 inputMessage: &pb3.Maps{},
964 inputText: `
965 int32_to_str: {
966 key: 100
967 }
968 bool_to_uint32: {
969 key: true
970 }
971 uint64_to_enum: {
972 key: 101
973 }
974 str_to_nested: {
975 key: "hello"
976 }
977 `,
978 wantMessage: &pb3.Maps{
979 Int32ToStr: map[int32]string{
980 100: "",
981 },
982 BoolToUint32: map[bool]uint32{
983 true: 0,
984 },
985 Uint64ToEnum: map[uint64]pb3.Enum{
986 101: pb3.Enum_ZERO,
987 },
988 StrToNested: map[string]*pb3.Nested{
989 "hello": {},
990 },
991 },
992 }, {
993 desc: "map contains missing key and value",
994 inputMessage: &pb3.Maps{},
995 inputText: `
996 int32_to_str: {}
997 bool_to_uint32: {}
998 uint64_to_enum: {}
999 str_to_nested: {}
1000 `,
1001 wantMessage: &pb3.Maps{
1002 Int32ToStr: map[int32]string{
1003 0: "",
1004 },
1005 BoolToUint32: map[bool]uint32{
1006 false: 0,
1007 },
1008 Uint64ToEnum: map[uint64]pb3.Enum{
1009 0: pb3.Enum_ZERO,
1010 },
1011 StrToNested: map[string]*pb3.Nested{
1012 "": {},
1013 },
1014 },
1015 }, {
1016 desc: "map contains overriding entries",
1017 inputMessage: &pb3.Maps{},
1018 inputText: `
1019 int32_to_str: {
1020 key: 0
1021 }
1022 int32_to_str: {
1023 value: "empty"
1024 }
1025 int32_to_str: {}
1026 `,
1027 wantMessage: &pb3.Maps{
1028 Int32ToStr: map[int32]string{
1029 0: "",
1030 },
1031 },
1032 }, {
1033 desc: "proto2 map field value contains invalid UTF-8",
1034 inputMessage: &pb2.Maps{},
1035 inputText: `int32_to_str: {
1036 key: 101
1037 value: "abc\xff"
1038 }
1039 `,
1040 wantMessage: &pb2.Maps{
1041 Int32ToStr: map[int32]string{101: "abc\xff"},
1042 },
1043 }, {
1044 desc: "proto2 map field key contains invalid UTF-8",
1045 inputMessage: &pb2.Maps{},
1046 inputText: `str_to_nested: {
1047 key: "abc\xff"
1048 value: {}
1049 }
1050 `,
1051 wantMessage: &pb2.Maps{
1052 StrToNested: map[string]*pb2.Nested{"abc\xff": {}},
1053 },
1054 }, {
1055 desc: "proto3 map field value contains invalid UTF-8",
1056 inputMessage: &pb3.Maps{},
1057 inputText: `int32_to_str: {
1058 key: 101
1059 value: "abc\xff"
1060 }
1061 `,
1062 wantErr: "contains invalid UTF-8",
1063 }, {
1064 desc: "proto3 map field key contains invalid UTF-8",
1065 inputMessage: &pb3.Maps{},
1066 inputText: `str_to_nested: {
1067 key: "abc\xff"
1068 value: {}
1069 }
1070 `,
1071 wantErr: "contains invalid UTF-8",
1072 }, {
1073 desc: "map contains unknown field",
1074 inputMessage: &pb3.Maps{},
1075 inputText: `
1076 int32_to_str: {
1077 key: 0
1078 value: "cero"
1079 unknown: "bad"
1080 }
1081 `,
1082 wantErr: `(line 5:3): unknown map entry field "unknown"`,
1083 }, {
1084 desc: "map contains extension-like key field",
1085 inputMessage: &pb3.Maps{},
1086 inputText: `
1087 int32_to_str: {
1088 [key]: 10
1089 value: "ten"
1090 }
1091 `,
1092 wantErr: `unknown map entry field "[key]"`,
1093 }, {
1094 desc: "map contains invalid key",
1095 inputMessage: &pb3.Maps{},
1096 inputText: `
1097 int32_to_str: {
1098 key: "invalid"
1099 value: "cero"
1100 }
1101 `,
1102 wantErr: "(line 3:8): invalid value for int32 type",
1103 }, {
1104 desc: "map contains invalid value",
1105 inputMessage: &pb3.Maps{},
1106 inputText: `
1107 int32_to_str: {
1108 key: 100
1109 value: 101
1110 }
1111 `,
1112 wantErr: "(line 4:10): invalid value for string type",
1113 }, {
1114 desc: "map contains invalid message value",
1115 inputMessage: &pb3.Maps{},
1116 inputText: `
1117 str_to_nested: {
1118 key: "one"
1119 value: 1
1120 }
1121 `,
1122 wantErr: "syntax error (line 4:10): unexpected token: 1",
1123 }, {
1124 desc: "map using mix of [] and repeated",
1125 inputMessage: &pb3.Maps{},
1126 inputText: `
1127 int32_to_str: {
1128 key: 1
1129 value: "one"
1130 }
1131 int32_to_str: [
1132 {
1133 key: 2
1134 value: "not this"
1135 },
1136 {
1137 },
1138 {
1139 key: 3
1140 value: "three"
1141 }
1142 ]
1143 int32_to_str: {
1144 key: 2
1145 value: "two"
1146 }
1147 `,
1148 wantMessage: &pb3.Maps{
1149 Int32ToStr: map[int32]string{
1150 0: "",
1151 1: "one",
1152 2: "two",
1153 3: "three",
1154 },
1155 },
1156 }, {
1157 desc: "required fields not set",
1158 inputMessage: &pb2.Requireds{},
1159 wantErr: "required field",
1160 }, {
1161 desc: "required field set",
1162 inputMessage: &pb2.PartialRequired{},
1163 inputText: "req_string: 'this is required'",
1164 wantMessage: &pb2.PartialRequired{
1165 ReqString: proto.String("this is required"),
1166 },
1167 }, {
1168 desc: "required fields partially set",
1169 inputMessage: &pb2.Requireds{},
1170 inputText: `
1171 req_bool: false
1172 req_sfixed64: 3203386110
1173 req_string: "hello"
1174 req_enum: ONE
1175 `,
1176 wantMessage: &pb2.Requireds{
1177 ReqBool: proto.Bool(false),
1178 ReqSfixed64: proto.Int64(0xbeefcafe),
1179 ReqString: proto.String("hello"),
1180 ReqEnum: pb2.Enum_ONE.Enum(),
1181 },
1182 wantErr: "required field",
1183 }, {
1184 desc: "required fields partially set with AllowPartial",
1185 umo: prototext.UnmarshalOptions{AllowPartial: true},
1186 inputMessage: &pb2.Requireds{},
1187 inputText: `
1188 req_bool: false
1189 req_sfixed64: 3203386110
1190 req_string: "hello"
1191 req_enum: ONE
1192 `,
1193 wantMessage: &pb2.Requireds{
1194 ReqBool: proto.Bool(false),
1195 ReqSfixed64: proto.Int64(0xbeefcafe),
1196 ReqString: proto.String("hello"),
1197 ReqEnum: pb2.Enum_ONE.Enum(),
1198 },
1199 }, {
1200 desc: "required fields all set",
1201 inputMessage: &pb2.Requireds{},
1202 inputText: `
1203 req_bool: false
1204 req_sfixed64: 0
1205 req_double: 0
1206 req_string: ""
1207 req_enum: ONE
1208 req_nested: {}
1209 `,
1210 wantMessage: &pb2.Requireds{
1211 ReqBool: proto.Bool(false),
1212 ReqSfixed64: proto.Int64(0),
1213 ReqDouble: proto.Float64(0),
1214 ReqString: proto.String(""),
1215 ReqEnum: pb2.Enum_ONE.Enum(),
1216 ReqNested: &pb2.Nested{},
1217 },
1218 }, {
1219 desc: "indirect required field",
1220 inputMessage: &pb2.IndirectRequired{},
1221 inputText: "opt_nested: {}",
1222 wantMessage: &pb2.IndirectRequired{
1223 OptNested: &pb2.NestedWithRequired{},
1224 },
1225 wantErr: "required field",
1226 }, {
1227 desc: "indirect required field with AllowPartial",
1228 umo: prototext.UnmarshalOptions{AllowPartial: true},
1229 inputMessage: &pb2.IndirectRequired{},
1230 inputText: "opt_nested: {}",
1231 wantMessage: &pb2.IndirectRequired{
1232 OptNested: &pb2.NestedWithRequired{},
1233 },
1234 }, {
1235 desc: "indirect required field in repeated",
1236 inputMessage: &pb2.IndirectRequired{},
1237 inputText: `
1238 rpt_nested: {
1239 req_string: "one"
1240 }
1241 rpt_nested: {}
1242 `,
1243 wantMessage: &pb2.IndirectRequired{
1244 RptNested: []*pb2.NestedWithRequired{
1245 {
1246 ReqString: proto.String("one"),
1247 },
1248 {},
1249 },
1250 },
1251 wantErr: "required field",
1252 }, {
1253 desc: "indirect required field in repeated with AllowPartial",
1254 umo: prototext.UnmarshalOptions{AllowPartial: true},
1255 inputMessage: &pb2.IndirectRequired{},
1256 inputText: `
1257 rpt_nested: {
1258 req_string: "one"
1259 }
1260 rpt_nested: {}
1261 `,
1262 wantMessage: &pb2.IndirectRequired{
1263 RptNested: []*pb2.NestedWithRequired{
1264 {
1265 ReqString: proto.String("one"),
1266 },
1267 {},
1268 },
1269 },
1270 }, {
1271 desc: "indirect required field in map",
1272 inputMessage: &pb2.IndirectRequired{},
1273 inputText: `
1274 str_to_nested: {
1275 key: "missing"
1276 }
1277 str_to_nested: {
1278 key: "contains"
1279 value: {
1280 req_string: "here"
1281 }
1282 }
1283 `,
1284 wantMessage: &pb2.IndirectRequired{
1285 StrToNested: map[string]*pb2.NestedWithRequired{
1286 "missing": &pb2.NestedWithRequired{},
1287 "contains": &pb2.NestedWithRequired{
1288 ReqString: proto.String("here"),
1289 },
1290 },
1291 },
1292 wantErr: "required field",
1293 }, {
1294 desc: "indirect required field in map with AllowPartial",
1295 umo: prototext.UnmarshalOptions{AllowPartial: true},
1296 inputMessage: &pb2.IndirectRequired{},
1297 inputText: `
1298 str_to_nested: {
1299 key: "missing"
1300 }
1301 str_to_nested: {
1302 key: "contains"
1303 value: {
1304 req_string: "here"
1305 }
1306 }
1307 `,
1308 wantMessage: &pb2.IndirectRequired{
1309 StrToNested: map[string]*pb2.NestedWithRequired{
1310 "missing": &pb2.NestedWithRequired{},
1311 "contains": &pb2.NestedWithRequired{
1312 ReqString: proto.String("here"),
1313 },
1314 },
1315 },
1316 }, {
1317 desc: "indirect required field in oneof",
1318 inputMessage: &pb2.IndirectRequired{},
1319 inputText: `oneof_nested: {}
1320 `,
1321 wantMessage: &pb2.IndirectRequired{
1322 Union: &pb2.IndirectRequired_OneofNested{
1323 OneofNested: &pb2.NestedWithRequired{},
1324 },
1325 },
1326 wantErr: "required field",
1327 }, {
1328 desc: "indirect required field in oneof with AllowPartial",
1329 umo: prototext.UnmarshalOptions{AllowPartial: true},
1330 inputMessage: &pb2.IndirectRequired{},
1331 inputText: `oneof_nested: {}
1332 `,
1333 wantMessage: &pb2.IndirectRequired{
1334 Union: &pb2.IndirectRequired_OneofNested{
1335 OneofNested: &pb2.NestedWithRequired{},
1336 },
1337 },
1338 }, {
1339 desc: "ignore reserved field",
1340 inputMessage: &pb2.Nests{},
1341 inputText: "reserved_field: 'ignore this'",
1342 wantMessage: &pb2.Nests{},
1343 }, {
1344 desc: "extensions of non-repeated fields",
1345 inputMessage: &pb2.Extensions{},
1346 inputText: `opt_string: "non-extension field"
1347 [pb2.opt_ext_bool]: true
1348 opt_bool: true
1349 [pb2.opt_ext_nested]: {
1350 opt_string: "nested in an extension"
1351 opt_nested: {
1352 opt_string: "another nested in an extension"
1353 }
1354 }
1355 [pb2.opt_ext_string]: "extension field"
1356 opt_int32: 42
1357 [pb2.opt_ext_enum]: TEN
1358 `,
1359 wantMessage: func() proto.Message {
1360 m := &pb2.Extensions{
1361 OptString: proto.String("non-extension field"),
1362 OptBool: proto.Bool(true),
1363 OptInt32: proto.Int32(42),
1364 }
1365 proto.SetExtension(m, pb2.E_OptExtBool, true)
1366 proto.SetExtension(m, pb2.E_OptExtString, "extension field")
1367 proto.SetExtension(m, pb2.E_OptExtEnum, pb2.Enum_TEN)
1368 proto.SetExtension(m, pb2.E_OptExtNested, &pb2.Nested{
1369 OptString: proto.String("nested in an extension"),
1370 OptNested: &pb2.Nested{
1371 OptString: proto.String("another nested in an extension"),
1372 },
1373 })
1374 return m
1375 }(),
1376 }, {
1377 desc: "extension field contains invalid UTF-8",
1378 inputMessage: &pb2.Extensions{},
1379 inputText: `[pb2.opt_ext_string]: "abc\xff"`,
1380 wantMessage: func() proto.Message {
1381 m := &pb2.Extensions{}
1382 proto.SetExtension(m, pb2.E_OptExtString, "abc\xff")
1383 return m
1384 }(),
1385 }, {
1386 desc: "extensions of repeated fields",
1387 inputMessage: &pb2.Extensions{},
1388 inputText: `[pb2.rpt_ext_enum]: TEN
1389 [pb2.rpt_ext_enum]: 101
1390 [pb2.rpt_ext_fixed32]: 42
1391 [pb2.rpt_ext_enum]: ONE
1392 [pb2.rpt_ext_nested]: {
1393 opt_string: "one"
1394 }
1395 [pb2.rpt_ext_nested]: {
1396 opt_string: "two"
1397 }
1398 [pb2.rpt_ext_fixed32]: 47
1399 [pb2.rpt_ext_nested]: {
1400 opt_string: "three"
1401 }
1402 `,
1403 wantMessage: func() proto.Message {
1404 m := &pb2.Extensions{}
1405 proto.SetExtension(m, pb2.E_RptExtEnum, []pb2.Enum{pb2.Enum_TEN, 101, pb2.Enum_ONE})
1406 proto.SetExtension(m, pb2.E_RptExtFixed32, []uint32{42, 47})
1407 proto.SetExtension(m, pb2.E_RptExtNested, []*pb2.Nested{
1408 &pb2.Nested{OptString: proto.String("one")},
1409 &pb2.Nested{OptString: proto.String("two")},
1410 &pb2.Nested{OptString: proto.String("three")},
1411 })
1412 return m
1413 }(),
1414 }, {
1415 desc: "extensions of non-repeated fields in another message",
1416 inputMessage: &pb2.Extensions{},
1417 inputText: `[pb2.ExtensionsContainer.opt_ext_bool]: true
1418 [pb2.ExtensionsContainer.opt_ext_enum]: TEN
1419 [pb2.ExtensionsContainer.opt_ext_nested]: {
1420 opt_string: "nested in an extension"
1421 opt_nested: {
1422 opt_string: "another nested in an extension"
1423 }
1424 }
1425 [pb2.ExtensionsContainer.opt_ext_string]: "extension field"
1426 `,
1427 wantMessage: func() proto.Message {
1428 m := &pb2.Extensions{}
1429 proto.SetExtension(m, pb2.E_ExtensionsContainer_OptExtBool, true)
1430 proto.SetExtension(m, pb2.E_ExtensionsContainer_OptExtString, "extension field")
1431 proto.SetExtension(m, pb2.E_ExtensionsContainer_OptExtEnum, pb2.Enum_TEN)
1432 proto.SetExtension(m, pb2.E_ExtensionsContainer_OptExtNested, &pb2.Nested{
1433 OptString: proto.String("nested in an extension"),
1434 OptNested: &pb2.Nested{
1435 OptString: proto.String("another nested in an extension"),
1436 },
1437 })
1438 return m
1439 }(),
1440 }, {
1441 desc: "extensions of repeated fields in another message",
1442 inputMessage: &pb2.Extensions{},
1443 inputText: `opt_string: "non-extension field"
1444 opt_bool: true
1445 opt_int32: 42
1446 [pb2.ExtensionsContainer.rpt_ext_nested]: {
1447 opt_string: "one"
1448 }
1449 [pb2.ExtensionsContainer.rpt_ext_enum]: TEN
1450 [pb2.ExtensionsContainer.rpt_ext_nested]: {
1451 opt_string: "two"
1452 }
1453 [pb2.ExtensionsContainer.rpt_ext_enum]: 101
1454 [pb2.ExtensionsContainer.rpt_ext_string]: "hello"
1455 [pb2.ExtensionsContainer.rpt_ext_enum]: ONE
1456 [pb2.ExtensionsContainer.rpt_ext_nested]: {
1457 opt_string: "three"
1458 }
1459 [pb2.ExtensionsContainer.rpt_ext_string]: "world"
1460 `,
1461 wantMessage: func() proto.Message {
1462 m := &pb2.Extensions{
1463 OptString: proto.String("non-extension field"),
1464 OptBool: proto.Bool(true),
1465 OptInt32: proto.Int32(42),
1466 }
1467 proto.SetExtension(m, pb2.E_ExtensionsContainer_RptExtEnum, []pb2.Enum{pb2.Enum_TEN, 101, pb2.Enum_ONE})
1468 proto.SetExtension(m, pb2.E_ExtensionsContainer_RptExtString, []string{"hello", "world"})
1469 proto.SetExtension(m, pb2.E_ExtensionsContainer_RptExtNested, []*pb2.Nested{
1470 &pb2.Nested{OptString: proto.String("one")},
1471 &pb2.Nested{OptString: proto.String("two")},
1472 &pb2.Nested{OptString: proto.String("three")},
1473 })
1474 return m
1475 }(),
1476 }, {
1477 desc: "invalid extension field name",
1478 inputMessage: &pb2.Extensions{},
1479 inputText: "[pb2.invalid_message_field]: true",
1480 wantErr: "unknown field",
1481 }, {
1482 desc: "MessageSet",
1483 inputMessage: &pb2.MessageSet{},
1484 inputText: `
1485 [pb2.MessageSetExtension]: {
1486 opt_string: "a messageset extension"
1487 }
1488 [pb2.MessageSetExtension.ext_nested]: {
1489 opt_string: "just a regular extension"
1490 }
1491 [pb2.MessageSetExtension.not_message_set_extension]: {
1492 opt_string: "not a messageset extension"
1493 }
1494 `,
1495 wantMessage: func() proto.Message {
1496 m := &pb2.MessageSet{}
1497 proto.SetExtension(m, pb2.E_MessageSetExtension_MessageSetExtension, &pb2.MessageSetExtension{
1498 OptString: proto.String("a messageset extension"),
1499 })
1500 proto.SetExtension(m, pb2.E_MessageSetExtension_NotMessageSetExtension, &pb2.MessageSetExtension{
1501 OptString: proto.String("not a messageset extension"),
1502 })
1503 proto.SetExtension(m, pb2.E_MessageSetExtension_ExtNested, &pb2.Nested{
1504 OptString: proto.String("just a regular extension"),
1505 })
1506 return m
1507 }(),
1508 skip: !flags.ProtoLegacy,
1509 }, {
1510 desc: "not real MessageSet 1",
1511 inputMessage: &pb2.FakeMessageSet{},
1512 inputText: `
1513 [pb2.FakeMessageSetExtension.message_set_extension]: {
1514 opt_string: "not a messageset extension"
1515 }
1516 `,
1517 wantMessage: func() proto.Message {
1518 m := &pb2.FakeMessageSet{}
1519 proto.SetExtension(m, pb2.E_FakeMessageSetExtension_MessageSetExtension, &pb2.FakeMessageSetExtension{
1520 OptString: proto.String("not a messageset extension"),
1521 })
1522 return m
1523 }(),
1524 skip: !flags.ProtoLegacy,
1525 }, {
1526 desc: "not real MessageSet 2",
1527 inputMessage: &pb2.FakeMessageSet{},
1528 inputText: `
1529 [pb2.FakeMessageSetExtension]: {
1530 opt_string: "not a messageset extension"
1531 }
1532 `,
1533 wantErr: `unable to resolve [[pb2.FakeMessageSetExtension]]: found wrong type`,
1534 skip: !flags.ProtoLegacy,
1535 }, {
1536 desc: "not real MessageSet 3",
1537 inputMessage: &pb2.MessageSet{},
1538 inputText: `
1539 [pb2.message_set_extension]: {
1540 opt_string: "another not a messageset extension"
1541 }`,
1542 wantMessage: func() proto.Message {
1543 m := &pb2.MessageSet{}
1544 proto.SetExtension(m, pb2.E_MessageSetExtension, &pb2.FakeMessageSetExtension{
1545 OptString: proto.String("another not a messageset extension"),
1546 })
1547 return m
1548 }(),
1549 skip: !flags.ProtoLegacy,
1550 }, {
1551 desc: "Any not expanded",
1552 inputMessage: &anypb.Any{},
1553 inputText: `
1554 type_url: "pb2.Nested"
1555 value: "some bytes"
1556 `,
1557 wantMessage: &anypb.Any{
1558 TypeUrl: "pb2.Nested",
1559 Value: []byte("some bytes"),
1560 },
1561 }, {
1562 desc: "Any not expanded missing value",
1563 inputMessage: &anypb.Any{},
1564 inputText: `type_url: "pb2.Nested"`,
1565 wantMessage: &anypb.Any{
1566 TypeUrl: "pb2.Nested",
1567 },
1568 }, {
1569 desc: "Any not expanded missing type_url",
1570 inputMessage: &anypb.Any{},
1571 inputText: `value: "some bytes"`,
1572 wantMessage: &anypb.Any{
1573 Value: []byte("some bytes"),
1574 },
1575 }, {
1576 desc: "Any expanded",
1577 inputMessage: &anypb.Any{},
1578 inputText: `
1579 [foobar/pb2.Nested]: {
1580 opt_string: "embedded inside Any"
1581 opt_nested: {
1582 opt_string: "inception"
1583 }
1584 }
1585 `,
1586 wantMessage: func() proto.Message {
1587 m := &pb2.Nested{
1588 OptString: proto.String("embedded inside Any"),
1589 OptNested: &pb2.Nested{
1590 OptString: proto.String("inception"),
1591 },
1592 }
1593 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1594 if err != nil {
1595 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1596 }
1597 return &anypb.Any{
1598 TypeUrl: "foobar/pb2.Nested",
1599 Value: b,
1600 }
1601 }(),
1602 }, {
1603 desc: "Any expanded with empty value",
1604 inputMessage: &anypb.Any{},
1605 inputText: `[foo.com/pb2.Nested]: {}`,
1606 wantMessage: &anypb.Any{
1607 TypeUrl: "foo.com/pb2.Nested",
1608 },
1609 }, {
1610 desc: "Any expanded with missing required",
1611 inputMessage: &anypb.Any{},
1612 inputText: `
1613 [pb2.PartialRequired]: {
1614 opt_string: "embedded inside Any"
1615 }
1616 `,
1617 wantMessage: func() proto.Message {
1618 m := &pb2.PartialRequired{
1619 OptString: proto.String("embedded inside Any"),
1620 }
1621 b, err := proto.MarshalOptions{
1622 AllowPartial: true,
1623 Deterministic: true,
1624 }.Marshal(m)
1625 if err != nil {
1626 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1627 }
1628 return &anypb.Any{
1629 TypeUrl: "pb2.PartialRequired",
1630 Value: b,
1631 }
1632 }(),
1633 }, {
1634 desc: "Any with invalid UTF-8",
1635 inputMessage: &anypb.Any{},
1636 inputText: `
1637 [pb3.Nested]: {
1638 s_string: "abc\xff"
1639 }
1640 `,
1641 wantErr: "contains invalid UTF-8",
1642 }, {
1643 desc: "Any expanded with unregistered type",
1644 umo: prototext.UnmarshalOptions{Resolver: new(protoregistry.Types)},
1645 inputMessage: &anypb.Any{},
1646 inputText: `[SomeMessage]: {}`,
1647 wantErr: "unable to resolve message [SomeMessage]",
1648 }, {
1649 desc: "Any expanded with invalid value",
1650 inputMessage: &anypb.Any{},
1651 inputText: `[pb2.Nested]: 123`,
1652 wantErr: "unexpected token: 123",
1653 }, {
1654 desc: "Any expanded with unknown fields",
1655 inputMessage: &anypb.Any{},
1656 inputText: `
1657 [pb2.Nested]: {}
1658 unknown: ""
1659 `,
1660 wantErr: `invalid field name "unknown" in google.protobuf.Any message`,
1661 }, {
1662 desc: "Any contains expanded and unexpanded fields",
1663 inputMessage: &anypb.Any{},
1664 inputText: `
1665 [pb2.Nested]: {}
1666 type_url: "pb2.Nested"
1667 `,
1668 wantErr: "(line 3:1): conflict with [pb2.Nested] field",
1669 }, {
1670 desc: "weak fields",
1671 inputMessage: &testpb.TestWeak{},
1672 inputText: `weak_message1:{a:1}`,
1673 wantMessage: func() *testpb.TestWeak {
1674 m := new(testpb.TestWeak)
1675 m.SetWeakMessage1(&weakpb.WeakImportMessage1{A: proto.Int32(1)})
1676 return m
1677 }(),
1678 skip: !flags.ProtoLegacy,
1679 }, {
1680 desc: "weak fields; unknown field",
1681 inputMessage: &testpb.TestWeak{},
1682 inputText: `weak_message1:{a:1} weak_message2:{a:1}`,
1683 wantErr: "unknown field: weak_message2",
1684 skip: !flags.ProtoLegacy,
1685 }}
1686
1687 for _, tt := range tests {
1688 tt := tt
1689 if tt.skip {
1690 continue
1691 }
1692 t.Run(tt.desc, func(t *testing.T) {
1693 err := tt.umo.Unmarshal([]byte(tt.inputText), tt.inputMessage)
1694 if err != nil {
1695 if tt.wantErr == "" {
1696 t.Errorf("Unmarshal() got unexpected error: %v", err)
1697 } else if !strings.Contains(err.Error(), tt.wantErr) {
1698 t.Errorf("Unmarshal() error got %q, want %q", err, tt.wantErr)
1699 }
1700 return
1701 }
1702 if tt.wantErr != "" {
1703 t.Errorf("Unmarshal() got nil error, want error %q", tt.wantErr)
1704 return
1705 }
1706 if tt.wantMessage != nil && !proto.Equal(tt.inputMessage, tt.wantMessage) {
1707 t.Errorf("Unmarshal()\n<got>\n%v\n<want>\n%v\n", tt.inputMessage, tt.wantMessage)
1708 }
1709 })
1710 }
1711 }
1712
View as plain text