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