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