您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 

707 行
16 KiB

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