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