OSDN Git Service

new repo
[bytom/vapor.git] / vendor / github.com / golang / protobuf / proto / text_parser_test.go
1 // Go support for Protocol Buffers - Google's data interchange format
2 //
3 // Copyright 2010 The Go Authors.  All rights reserved.
4 // https://github.com/golang/protobuf
5 //
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions are
8 // met:
9 //
10 //     * Redistributions of source code must retain the above copyright
11 // notice, this list of conditions and the following disclaimer.
12 //     * Redistributions in binary form must reproduce the above
13 // copyright notice, this list of conditions and the following disclaimer
14 // in the documentation and/or other materials provided with the
15 // distribution.
16 //     * Neither the name of Google Inc. nor the names of its
17 // contributors may be used to endorse or promote products derived from
18 // this software without specific prior written permission.
19 //
20 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
32 package proto_test
33
34 import (
35         "math"
36         "reflect"
37         "testing"
38
39         . "github.com/golang/protobuf/proto"
40         proto3pb "github.com/golang/protobuf/proto/proto3_proto"
41         . "github.com/golang/protobuf/proto/testdata"
42 )
43
44 type UnmarshalTextTest struct {
45         in  string
46         err string // if "", no error expected
47         out *MyMessage
48 }
49
50 func buildExtStructTest(text string) UnmarshalTextTest {
51         msg := &MyMessage{
52                 Count: Int32(42),
53         }
54         SetExtension(msg, E_Ext_More, &Ext{
55                 Data: String("Hello, world!"),
56         })
57         return UnmarshalTextTest{in: text, out: msg}
58 }
59
60 func buildExtDataTest(text string) UnmarshalTextTest {
61         msg := &MyMessage{
62                 Count: Int32(42),
63         }
64         SetExtension(msg, E_Ext_Text, String("Hello, world!"))
65         SetExtension(msg, E_Ext_Number, Int32(1729))
66         return UnmarshalTextTest{in: text, out: msg}
67 }
68
69 func buildExtRepStringTest(text string) UnmarshalTextTest {
70         msg := &MyMessage{
71                 Count: Int32(42),
72         }
73         if err := SetExtension(msg, E_Greeting, []string{"bula", "hola"}); err != nil {
74                 panic(err)
75         }
76         return UnmarshalTextTest{in: text, out: msg}
77 }
78
79 var unMarshalTextTests = []UnmarshalTextTest{
80         // Basic
81         {
82                 in: " count:42\n  name:\"Dave\" ",
83                 out: &MyMessage{
84                         Count: Int32(42),
85                         Name:  String("Dave"),
86                 },
87         },
88
89         // Empty quoted string
90         {
91                 in: `count:42 name:""`,
92                 out: &MyMessage{
93                         Count: Int32(42),
94                         Name:  String(""),
95                 },
96         },
97
98         // Quoted string concatenation with double quotes
99         {
100                 in: `count:42 name: "My name is "` + "\n" + `"elsewhere"`,
101                 out: &MyMessage{
102                         Count: Int32(42),
103                         Name:  String("My name is elsewhere"),
104                 },
105         },
106
107         // Quoted string concatenation with single quotes
108         {
109                 in: "count:42 name: 'My name is '\n'elsewhere'",
110                 out: &MyMessage{
111                         Count: Int32(42),
112                         Name:  String("My name is elsewhere"),
113                 },
114         },
115
116         // Quoted string concatenations with mixed quotes
117         {
118                 in: "count:42 name: 'My name is '\n\"elsewhere\"",
119                 out: &MyMessage{
120                         Count: Int32(42),
121                         Name:  String("My name is elsewhere"),
122                 },
123         },
124         {
125                 in: "count:42 name: \"My name is \"\n'elsewhere'",
126                 out: &MyMessage{
127                         Count: Int32(42),
128                         Name:  String("My name is elsewhere"),
129                 },
130         },
131
132         // Quoted string with escaped apostrophe
133         {
134                 in: `count:42 name: "HOLIDAY - New Year\'s Day"`,
135                 out: &MyMessage{
136                         Count: Int32(42),
137                         Name:  String("HOLIDAY - New Year's Day"),
138                 },
139         },
140
141         // Quoted string with single quote
142         {
143                 in: `count:42 name: 'Roger "The Ramster" Ramjet'`,
144                 out: &MyMessage{
145                         Count: Int32(42),
146                         Name:  String(`Roger "The Ramster" Ramjet`),
147                 },
148         },
149
150         // Quoted string with all the accepted special characters from the C++ test
151         {
152                 in: `count:42 name: ` + "\"\\\"A string with \\' characters \\n and \\r newlines and \\t tabs and \\001 slashes \\\\ and  multiple   spaces\"",
153                 out: &MyMessage{
154                         Count: Int32(42),
155                         Name:  String("\"A string with ' characters \n and \r newlines and \t tabs and \001 slashes \\ and  multiple   spaces"),
156                 },
157         },
158
159         // Quoted string with quoted backslash
160         {
161                 in: `count:42 name: "\\'xyz"`,
162                 out: &MyMessage{
163                         Count: Int32(42),
164                         Name:  String(`\'xyz`),
165                 },
166         },
167
168         // Quoted string with UTF-8 bytes.
169         {
170                 in: "count:42 name: '\303\277\302\201\xAB'",
171                 out: &MyMessage{
172                         Count: Int32(42),
173                         Name:  String("\303\277\302\201\xAB"),
174                 },
175         },
176
177         // Bad quoted string
178         {
179                 in:  `inner: < host: "\0" >` + "\n",
180                 err: `line 1.15: invalid quoted string "\0": \0 requires 2 following digits`,
181         },
182
183         // Number too large for int64
184         {
185                 in:  "count: 1 others { key: 123456789012345678901 }",
186                 err: "line 1.23: invalid int64: 123456789012345678901",
187         },
188
189         // Number too large for int32
190         {
191                 in:  "count: 1234567890123",
192                 err: "line 1.7: invalid int32: 1234567890123",
193         },
194
195         // Number in hexadecimal
196         {
197                 in: "count: 0x2beef",
198                 out: &MyMessage{
199                         Count: Int32(0x2beef),
200                 },
201         },
202
203         // Number in octal
204         {
205                 in: "count: 024601",
206                 out: &MyMessage{
207                         Count: Int32(024601),
208                 },
209         },
210
211         // Floating point number with "f" suffix
212         {
213                 in: "count: 4 others:< weight: 17.0f >",
214                 out: &MyMessage{
215                         Count: Int32(4),
216                         Others: []*OtherMessage{
217                                 {
218                                         Weight: Float32(17),
219                                 },
220                         },
221                 },
222         },
223
224         // Floating point positive infinity
225         {
226                 in: "count: 4 bigfloat: inf",
227                 out: &MyMessage{
228                         Count:    Int32(4),
229                         Bigfloat: Float64(math.Inf(1)),
230                 },
231         },
232
233         // Floating point negative infinity
234         {
235                 in: "count: 4 bigfloat: -inf",
236                 out: &MyMessage{
237                         Count:    Int32(4),
238                         Bigfloat: Float64(math.Inf(-1)),
239                 },
240         },
241
242         // Number too large for float32
243         {
244                 in:  "others:< weight: 12345678901234567890123456789012345678901234567890 >",
245                 err: "line 1.17: invalid float32: 12345678901234567890123456789012345678901234567890",
246         },
247
248         // Number posing as a quoted string
249         {
250                 in:  `inner: < host: 12 >` + "\n",
251                 err: `line 1.15: invalid string: 12`,
252         },
253
254         // Quoted string posing as int32
255         {
256                 in:  `count: "12"`,
257                 err: `line 1.7: invalid int32: "12"`,
258         },
259
260         // Quoted string posing a float32
261         {
262                 in:  `others:< weight: "17.4" >`,
263                 err: `line 1.17: invalid float32: "17.4"`,
264         },
265
266         // Enum
267         {
268                 in: `count:42 bikeshed: BLUE`,
269                 out: &MyMessage{
270                         Count:    Int32(42),
271                         Bikeshed: MyMessage_BLUE.Enum(),
272                 },
273         },
274
275         // Repeated field
276         {
277                 in: `count:42 pet: "horsey" pet:"bunny"`,
278                 out: &MyMessage{
279                         Count: Int32(42),
280                         Pet:   []string{"horsey", "bunny"},
281                 },
282         },
283
284         // Repeated field with list notation
285         {
286                 in: `count:42 pet: ["horsey", "bunny"]`,
287                 out: &MyMessage{
288                         Count: Int32(42),
289                         Pet:   []string{"horsey", "bunny"},
290                 },
291         },
292
293         // Repeated message with/without colon and <>/{}
294         {
295                 in: `count:42 others:{} others{} others:<> others:{}`,
296                 out: &MyMessage{
297                         Count: Int32(42),
298                         Others: []*OtherMessage{
299                                 {},
300                                 {},
301                                 {},
302                                 {},
303                         },
304                 },
305         },
306
307         // Missing colon for inner message
308         {
309                 in: `count:42 inner < host: "cauchy.syd" >`,
310                 out: &MyMessage{
311                         Count: Int32(42),
312                         Inner: &InnerMessage{
313                                 Host: String("cauchy.syd"),
314                         },
315                 },
316         },
317
318         // Missing colon for string field
319         {
320                 in:  `name "Dave"`,
321                 err: `line 1.5: expected ':', found "\"Dave\""`,
322         },
323
324         // Missing colon for int32 field
325         {
326                 in:  `count 42`,
327                 err: `line 1.6: expected ':', found "42"`,
328         },
329
330         // Missing required field
331         {
332                 in:  `name: "Pawel"`,
333                 err: `proto: required field "testdata.MyMessage.count" not set`,
334                 out: &MyMessage{
335                         Name: String("Pawel"),
336                 },
337         },
338
339         // Missing required field in a required submessage
340         {
341                 in:  `count: 42 we_must_go_deeper < leo_finally_won_an_oscar <> >`,
342                 err: `proto: required field "testdata.InnerMessage.host" not set`,
343                 out: &MyMessage{
344                         Count:          Int32(42),
345                         WeMustGoDeeper: &RequiredInnerMessage{LeoFinallyWonAnOscar: &InnerMessage{}},
346                 },
347         },
348
349         // Repeated non-repeated field
350         {
351                 in:  `name: "Rob" name: "Russ"`,
352                 err: `line 1.12: non-repeated field "name" was repeated`,
353         },
354
355         // Group
356         {
357                 in: `count: 17 SomeGroup { group_field: 12 }`,
358                 out: &MyMessage{
359                         Count: Int32(17),
360                         Somegroup: &MyMessage_SomeGroup{
361                                 GroupField: Int32(12),
362                         },
363                 },
364         },
365
366         // Semicolon between fields
367         {
368                 in: `count:3;name:"Calvin"`,
369                 out: &MyMessage{
370                         Count: Int32(3),
371                         Name:  String("Calvin"),
372                 },
373         },
374         // Comma between fields
375         {
376                 in: `count:4,name:"Ezekiel"`,
377                 out: &MyMessage{
378                         Count: Int32(4),
379                         Name:  String("Ezekiel"),
380                 },
381         },
382
383         // Boolean false
384         {
385                 in: `count:42 inner { host: "example.com" connected: false }`,
386                 out: &MyMessage{
387                         Count: Int32(42),
388                         Inner: &InnerMessage{
389                                 Host:      String("example.com"),
390                                 Connected: Bool(false),
391                         },
392                 },
393         },
394         // Boolean true
395         {
396                 in: `count:42 inner { host: "example.com" connected: true }`,
397                 out: &MyMessage{
398                         Count: Int32(42),
399                         Inner: &InnerMessage{
400                                 Host:      String("example.com"),
401                                 Connected: Bool(true),
402                         },
403                 },
404         },
405         // Boolean 0
406         {
407                 in: `count:42 inner { host: "example.com" connected: 0 }`,
408                 out: &MyMessage{
409                         Count: Int32(42),
410                         Inner: &InnerMessage{
411                                 Host:      String("example.com"),
412                                 Connected: Bool(false),
413                         },
414                 },
415         },
416         // Boolean 1
417         {
418                 in: `count:42 inner { host: "example.com" connected: 1 }`,
419                 out: &MyMessage{
420                         Count: Int32(42),
421                         Inner: &InnerMessage{
422                                 Host:      String("example.com"),
423                                 Connected: Bool(true),
424                         },
425                 },
426         },
427         // Boolean f
428         {
429                 in: `count:42 inner { host: "example.com" connected: f }`,
430                 out: &MyMessage{
431                         Count: Int32(42),
432                         Inner: &InnerMessage{
433                                 Host:      String("example.com"),
434                                 Connected: Bool(false),
435                         },
436                 },
437         },
438         // Boolean t
439         {
440                 in: `count:42 inner { host: "example.com" connected: t }`,
441                 out: &MyMessage{
442                         Count: Int32(42),
443                         Inner: &InnerMessage{
444                                 Host:      String("example.com"),
445                                 Connected: Bool(true),
446                         },
447                 },
448         },
449         // Boolean False
450         {
451                 in: `count:42 inner { host: "example.com" connected: False }`,
452                 out: &MyMessage{
453                         Count: Int32(42),
454                         Inner: &InnerMessage{
455                                 Host:      String("example.com"),
456                                 Connected: Bool(false),
457                         },
458                 },
459         },
460         // Boolean True
461         {
462                 in: `count:42 inner { host: "example.com" connected: True }`,
463                 out: &MyMessage{
464                         Count: Int32(42),
465                         Inner: &InnerMessage{
466                                 Host:      String("example.com"),
467                                 Connected: Bool(true),
468                         },
469                 },
470         },
471
472         // Extension
473         buildExtStructTest(`count: 42 [testdata.Ext.more]:<data:"Hello, world!" >`),
474         buildExtStructTest(`count: 42 [testdata.Ext.more] {data:"Hello, world!"}`),
475         buildExtDataTest(`count: 42 [testdata.Ext.text]:"Hello, world!" [testdata.Ext.number]:1729`),
476         buildExtRepStringTest(`count: 42 [testdata.greeting]:"bula" [testdata.greeting]:"hola"`),
477
478         // Big all-in-one
479         {
480                 in: "count:42  # Meaning\n" +
481                         `name:"Dave" ` +
482                         `quote:"\"I didn't want to go.\"" ` +
483                         `pet:"bunny" ` +
484                         `pet:"kitty" ` +
485                         `pet:"horsey" ` +
486                         `inner:<` +
487                         `  host:"footrest.syd" ` +
488                         `  port:7001 ` +
489                         `  connected:true ` +
490                         `> ` +
491                         `others:<` +
492                         `  key:3735928559 ` +
493                         `  value:"\x01A\a\f" ` +
494                         `> ` +
495                         `others:<` +
496                         "  weight:58.9  # Atomic weight of Co\n" +
497                         `  inner:<` +
498                         `    host:"lesha.mtv" ` +
499                         `    port:8002 ` +
500                         `  >` +
501                         `>`,
502                 out: &MyMessage{
503                         Count: Int32(42),
504                         Name:  String("Dave"),
505                         Quote: String(`"I didn't want to go."`),
506                         Pet:   []string{"bunny", "kitty", "horsey"},
507                         Inner: &InnerMessage{
508                                 Host:      String("footrest.syd"),
509                                 Port:      Int32(7001),
510                                 Connected: Bool(true),
511                         },
512                         Others: []*OtherMessage{
513                                 {
514                                         Key:   Int64(3735928559),
515                                         Value: []byte{0x1, 'A', '\a', '\f'},
516                                 },
517                                 {
518                                         Weight: Float32(58.9),
519                                         Inner: &InnerMessage{
520                                                 Host: String("lesha.mtv"),
521                                                 Port: Int32(8002),
522                                         },
523                                 },
524                         },
525                 },
526         },
527 }
528
529 func TestUnmarshalText(t *testing.T) {
530         for i, test := range unMarshalTextTests {
531                 pb := new(MyMessage)
532                 err := UnmarshalText(test.in, pb)
533                 if test.err == "" {
534                         // We don't expect failure.
535                         if err != nil {
536                                 t.Errorf("Test %d: Unexpected error: %v", i, err)
537                         } else if !reflect.DeepEqual(pb, test.out) {
538                                 t.Errorf("Test %d: Incorrect populated \nHave: %v\nWant: %v",
539                                         i, pb, test.out)
540                         }
541                 } else {
542                         // We do expect failure.
543                         if err == nil {
544                                 t.Errorf("Test %d: Didn't get expected error: %v", i, test.err)
545                         } else if err.Error() != test.err {
546                                 t.Errorf("Test %d: Incorrect error.\nHave: %v\nWant: %v",
547                                         i, err.Error(), test.err)
548                         } else if _, ok := err.(*RequiredNotSetError); ok && test.out != nil && !reflect.DeepEqual(pb, test.out) {
549                                 t.Errorf("Test %d: Incorrect populated \nHave: %v\nWant: %v",
550                                         i, pb, test.out)
551                         }
552                 }
553         }
554 }
555
556 func TestUnmarshalTextCustomMessage(t *testing.T) {
557         msg := &textMessage{}
558         if err := UnmarshalText("custom", msg); err != nil {
559                 t.Errorf("Unexpected error from custom unmarshal: %v", err)
560         }
561         if UnmarshalText("not custom", msg) == nil {
562                 t.Errorf("Didn't get expected error from custom unmarshal")
563         }
564 }
565
566 // Regression test; this caused a panic.
567 func TestRepeatedEnum(t *testing.T) {
568         pb := new(RepeatedEnum)
569         if err := UnmarshalText("color: RED", pb); err != nil {
570                 t.Fatal(err)
571         }
572         exp := &RepeatedEnum{
573                 Color: []RepeatedEnum_Color{RepeatedEnum_RED},
574         }
575         if !Equal(pb, exp) {
576                 t.Errorf("Incorrect populated \nHave: %v\nWant: %v", pb, exp)
577         }
578 }
579
580 func TestProto3TextParsing(t *testing.T) {
581         m := new(proto3pb.Message)
582         const in = `name: "Wallace" true_scotsman: true`
583         want := &proto3pb.Message{
584                 Name:         "Wallace",
585                 TrueScotsman: true,
586         }
587         if err := UnmarshalText(in, m); err != nil {
588                 t.Fatal(err)
589         }
590         if !Equal(m, want) {
591                 t.Errorf("\n got %v\nwant %v", m, want)
592         }
593 }
594
595 func TestMapParsing(t *testing.T) {
596         m := new(MessageWithMap)
597         const in = `name_mapping:<key:1234 value:"Feist"> name_mapping:<key:1 value:"Beatles">` +
598                 `msg_mapping:<key:-4, value:<f: 2.0>,>` + // separating commas are okay
599                 `msg_mapping<key:-2 value<f: 4.0>>` + // no colon after "value"
600                 `msg_mapping:<value:<f: 5.0>>` + // omitted key
601                 `msg_mapping:<key:1>` + // omitted value
602                 `byte_mapping:<key:true value:"so be it">` +
603                 `byte_mapping:<>` // omitted key and value
604         want := &MessageWithMap{
605                 NameMapping: map[int32]string{
606                         1:    "Beatles",
607                         1234: "Feist",
608                 },
609                 MsgMapping: map[int64]*FloatingPoint{
610                         -4: {F: Float64(2.0)},
611                         -2: {F: Float64(4.0)},
612                         0:  {F: Float64(5.0)},
613                         1:  nil,
614                 },
615                 ByteMapping: map[bool][]byte{
616                         false: nil,
617                         true:  []byte("so be it"),
618                 },
619         }
620         if err := UnmarshalText(in, m); err != nil {
621                 t.Fatal(err)
622         }
623         if !Equal(m, want) {
624                 t.Errorf("\n got %v\nwant %v", m, want)
625         }
626 }
627
628 func TestOneofParsing(t *testing.T) {
629         const in = `name:"Shrek"`
630         m := new(Communique)
631         want := &Communique{Union: &Communique_Name{"Shrek"}}
632         if err := UnmarshalText(in, m); err != nil {
633                 t.Fatal(err)
634         }
635         if !Equal(m, want) {
636                 t.Errorf("\n got %v\nwant %v", m, want)
637         }
638
639         const inOverwrite = `name:"Shrek" number:42`
640         m = new(Communique)
641         testErr := "line 1.13: field 'number' would overwrite already parsed oneof 'Union'"
642         if err := UnmarshalText(inOverwrite, m); err == nil {
643                 t.Errorf("TestOneofParsing: Didn't get expected error: %v", testErr)
644         } else if err.Error() != testErr {
645                 t.Errorf("TestOneofParsing: Incorrect error.\nHave: %v\nWant: %v",
646                         err.Error(), testErr)
647         }
648
649 }
650
651 var benchInput string
652
653 func init() {
654         benchInput = "count: 4\n"
655         for i := 0; i < 1000; i++ {
656                 benchInput += "pet: \"fido\"\n"
657         }
658
659         // Check it is valid input.
660         pb := new(MyMessage)
661         err := UnmarshalText(benchInput, pb)
662         if err != nil {
663                 panic("Bad benchmark input: " + err.Error())
664         }
665 }
666
667 func BenchmarkUnmarshalText(b *testing.B) {
668         pb := new(MyMessage)
669         for i := 0; i < b.N; i++ {
670                 UnmarshalText(benchInput, pb)
671         }
672         b.SetBytes(int64(len(benchInput)))
673 }