github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/golang/protobuf/proto/text_parser_test.go (about) 1 // Go support for Protocol Buffers - Google's data interchange format 2 // 3 // Copyright 2010 The Go Authors. All rights reserved. 4 // https://yougam/libraries/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/insionng/yougam/libraries/golang/protobuf/proto" 40 proto3pb "github.com/insionng/yougam/libraries/golang/protobuf/proto/proto3_proto" 41 . "github.com/insionng/yougam/libraries/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 // Extension 384 buildExtStructTest(`count: 42 [testdata.Ext.more]:<data:"Hello, world!" >`), 385 buildExtStructTest(`count: 42 [testdata.Ext.more] {data:"Hello, world!"}`), 386 buildExtDataTest(`count: 42 [testdata.Ext.text]:"Hello, world!" [testdata.Ext.number]:1729`), 387 buildExtRepStringTest(`count: 42 [testdata.greeting]:"bula" [testdata.greeting]:"hola"`), 388 389 // Big all-in-one 390 { 391 in: "count:42 # Meaning\n" + 392 `name:"Dave" ` + 393 `quote:"\"I didn't want to go.\"" ` + 394 `pet:"bunny" ` + 395 `pet:"kitty" ` + 396 `pet:"horsey" ` + 397 `inner:<` + 398 ` host:"footrest.syd" ` + 399 ` port:7001 ` + 400 ` connected:true ` + 401 `> ` + 402 `others:<` + 403 ` key:3735928559 ` + 404 ` value:"\x01A\a\f" ` + 405 `> ` + 406 `others:<` + 407 " weight:58.9 # Atomic weight of Co\n" + 408 ` inner:<` + 409 ` host:"lesha.mtv" ` + 410 ` port:8002 ` + 411 ` >` + 412 `>`, 413 out: &MyMessage{ 414 Count: Int32(42), 415 Name: String("Dave"), 416 Quote: String(`"I didn't want to go."`), 417 Pet: []string{"bunny", "kitty", "horsey"}, 418 Inner: &InnerMessage{ 419 Host: String("footrest.syd"), 420 Port: Int32(7001), 421 Connected: Bool(true), 422 }, 423 Others: []*OtherMessage{ 424 { 425 Key: Int64(3735928559), 426 Value: []byte{0x1, 'A', '\a', '\f'}, 427 }, 428 { 429 Weight: Float32(58.9), 430 Inner: &InnerMessage{ 431 Host: String("lesha.mtv"), 432 Port: Int32(8002), 433 }, 434 }, 435 }, 436 }, 437 }, 438 } 439 440 func TestUnmarshalText(t *testing.T) { 441 for i, test := range unMarshalTextTests { 442 pb := new(MyMessage) 443 err := UnmarshalText(test.in, pb) 444 if test.err == "" { 445 // We don't expect failure. 446 if err != nil { 447 t.Errorf("Test %d: Unexpected error: %v", i, err) 448 } else if !reflect.DeepEqual(pb, test.out) { 449 t.Errorf("Test %d: Incorrect populated \nHave: %v\nWant: %v", 450 i, pb, test.out) 451 } 452 } else { 453 // We do expect failure. 454 if err == nil { 455 t.Errorf("Test %d: Didn't get expected error: %v", i, test.err) 456 } else if err.Error() != test.err { 457 t.Errorf("Test %d: Incorrect error.\nHave: %v\nWant: %v", 458 i, err.Error(), test.err) 459 } else if _, ok := err.(*RequiredNotSetError); ok && test.out != nil && !reflect.DeepEqual(pb, test.out) { 460 t.Errorf("Test %d: Incorrect populated \nHave: %v\nWant: %v", 461 i, pb, test.out) 462 } 463 } 464 } 465 } 466 467 func TestUnmarshalTextCustomMessage(t *testing.T) { 468 msg := &textMessage{} 469 if err := UnmarshalText("custom", msg); err != nil { 470 t.Errorf("Unexpected error from custom unmarshal: %v", err) 471 } 472 if UnmarshalText("not custom", msg) == nil { 473 t.Errorf("Didn't get expected error from custom unmarshal") 474 } 475 } 476 477 // Regression test; this caused a panic. 478 func TestRepeatedEnum(t *testing.T) { 479 pb := new(RepeatedEnum) 480 if err := UnmarshalText("color: RED", pb); err != nil { 481 t.Fatal(err) 482 } 483 exp := &RepeatedEnum{ 484 Color: []RepeatedEnum_Color{RepeatedEnum_RED}, 485 } 486 if !Equal(pb, exp) { 487 t.Errorf("Incorrect populated \nHave: %v\nWant: %v", pb, exp) 488 } 489 } 490 491 func TestProto3TextParsing(t *testing.T) { 492 m := new(proto3pb.Message) 493 const in = `name: "Wallace" true_scotsman: true` 494 want := &proto3pb.Message{ 495 Name: "Wallace", 496 TrueScotsman: true, 497 } 498 if err := UnmarshalText(in, m); err != nil { 499 t.Fatal(err) 500 } 501 if !Equal(m, want) { 502 t.Errorf("\n got %v\nwant %v", m, want) 503 } 504 } 505 506 func TestMapParsing(t *testing.T) { 507 m := new(MessageWithMap) 508 const in = `name_mapping:<key:1234 value:"Feist"> name_mapping:<key:1 value:"Beatles">` + 509 `msg_mapping:<key:-4, value:<f: 2.0>,>` + // separating commas are okay 510 `msg_mapping<key:-2 value<f: 4.0>>` + // no colon after "value" 511 `byte_mapping:<key:true value:"so be it">` 512 want := &MessageWithMap{ 513 NameMapping: map[int32]string{ 514 1: "Beatles", 515 1234: "Feist", 516 }, 517 MsgMapping: map[int64]*FloatingPoint{ 518 -4: {F: Float64(2.0)}, 519 -2: {F: Float64(4.0)}, 520 }, 521 ByteMapping: map[bool][]byte{ 522 true: []byte("so be it"), 523 }, 524 } 525 if err := UnmarshalText(in, m); err != nil { 526 t.Fatal(err) 527 } 528 if !Equal(m, want) { 529 t.Errorf("\n got %v\nwant %v", m, want) 530 } 531 } 532 533 func TestOneofParsing(t *testing.T) { 534 const in = `name:"Shrek"` 535 m := new(Communique) 536 want := &Communique{Union: &Communique_Name{"Shrek"}} 537 if err := UnmarshalText(in, m); err != nil { 538 t.Fatal(err) 539 } 540 if !Equal(m, want) { 541 t.Errorf("\n got %v\nwant %v", m, want) 542 } 543 } 544 545 var benchInput string 546 547 func init() { 548 benchInput = "count: 4\n" 549 for i := 0; i < 1000; i++ { 550 benchInput += "pet: \"fido\"\n" 551 } 552 553 // Check it is valid input. 554 pb := new(MyMessage) 555 err := UnmarshalText(benchInput, pb) 556 if err != nil { 557 panic("Bad benchmark input: " + err.Error()) 558 } 559 } 560 561 func BenchmarkUnmarshalText(b *testing.B) { 562 pb := new(MyMessage) 563 for i := 0; i < b.N; i++ { 564 UnmarshalText(benchInput, pb) 565 } 566 b.SetBytes(int64(len(benchInput))) 567 }