blob: cfc0db6ec99e54dcf1e04cd73c02e716d8e11e76 [file] [log] [blame]
Rob Pikeaaa3a622010-03-20 22:32:34 -07001// Go support for Protocol Buffers - Google's data interchange format
2//
David Symondsee6e9c52012-11-29 08:51:07 +11003// Copyright 2010 The Go Authors. All rights reserved.
David Symonds558f13f2014-11-24 10:28:53 +11004// https://github.com/golang/protobuf
Rob Pikeaaa3a622010-03-20 22:32:34 -07005//
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
32package proto_test
33
34import (
David Symonds31faaca2012-12-06 15:51:11 +110035 "math"
Rob Pikeaaa3a622010-03-20 22:32:34 -070036 "reflect"
37 "testing"
David Symonds20c73662012-01-20 07:32:21 +110038
David Symonds558f13f2014-11-24 10:28:53 +110039 . "github.com/golang/protobuf/proto"
David Symondsa8de2842015-03-20 16:24:29 +110040 proto3pb "github.com/golang/protobuf/proto/proto3_proto"
41 . "github.com/golang/protobuf/proto/testdata"
Rob Pikeaaa3a622010-03-20 22:32:34 -070042)
43
44type UnmarshalTextTest struct {
David Symonds5ed980c2011-11-29 09:52:21 +110045 in string
46 err string // if "", no error expected
47 out *MyMessage
Rob Pikeaaa3a622010-03-20 22:32:34 -070048}
49
David Symonds54531052011-12-08 12:00:31 +110050func 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
60func 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
David Symonds61826da2012-05-05 09:31:28 +100069func 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
Rob Pikeaaa3a622010-03-20 22:32:34 -070079var unMarshalTextTests = []UnmarshalTextTest{
80 // Basic
David Symonds9f402812011-04-28 18:08:44 +100081 {
Rob Pikeaaa3a622010-03-20 22:32:34 -070082 in: " count:42\n name:\"Dave\" ",
83 out: &MyMessage{
84 Count: Int32(42),
Rob Pike9caa5b92010-05-11 16:04:57 -070085 Name: String("Dave"),
Rob Pikeaaa3a622010-03-20 22:32:34 -070086 },
87 },
88
89 // Empty quoted string
David Symonds9f402812011-04-28 18:08:44 +100090 {
Rob Pikeaaa3a622010-03-20 22:32:34 -070091 in: `count:42 name:""`,
92 out: &MyMessage{
93 Count: Int32(42),
Rob Pike9caa5b92010-05-11 16:04:57 -070094 Name: String(""),
Rob Pikeaaa3a622010-03-20 22:32:34 -070095 },
96 },
97
98 // Quoted string concatenation
David Symonds9f402812011-04-28 18:08:44 +100099 {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700100 in: `count:42 name: "My name is "` + "\n" + `"elsewhere"`,
101 out: &MyMessage{
102 Count: Int32(42),
Rob Pike9caa5b92010-05-11 16:04:57 -0700103 Name: String("My name is elsewhere"),
Rob Pikeaaa3a622010-03-20 22:32:34 -0700104 },
105 },
106
David Symonds183124e2012-03-23 13:20:23 +1100107 // Quoted string with escaped apostrophe
108 {
109 in: `count:42 name: "HOLIDAY - New Year\'s Day"`,
110 out: &MyMessage{
111 Count: Int32(42),
112 Name: String("HOLIDAY - New Year's Day"),
113 },
114 },
115
David Symonds162d0032012-06-28 09:44:46 -0700116 // Quoted string with single quote
117 {
118 in: `count:42 name: 'Roger "The Ramster" Ramjet'`,
119 out: &MyMessage{
120 Count: Int32(42),
121 Name: String(`Roger "The Ramster" Ramjet`),
122 },
123 },
124
David Symondsfa94a1e2012-09-24 13:21:49 +1000125 // Quoted string with all the accepted special characters from the C++ test
126 {
127 in: `count:42 name: ` + "\"\\\"A string with \\' characters \\n and \\r newlines and \\t tabs and \\001 slashes \\\\ and multiple spaces\"",
128 out: &MyMessage{
129 Count: Int32(42),
130 Name: String("\"A string with ' characters \n and \r newlines and \t tabs and \001 slashes \\ and multiple spaces"),
131 },
132 },
133
134 // Quoted string with quoted backslash
135 {
136 in: `count:42 name: "\\'xyz"`,
137 out: &MyMessage{
138 Count: Int32(42),
139 Name: String(`\'xyz`),
140 },
141 },
142
143 // Quoted string with UTF-8 bytes.
144 {
145 in: "count:42 name: '\303\277\302\201\xAB'",
146 out: &MyMessage{
147 Count: Int32(42),
148 Name: String("\303\277\302\201\xAB"),
149 },
150 },
151
Rob Pikeaaa3a622010-03-20 22:32:34 -0700152 // Bad quoted string
David Symonds9f402812011-04-28 18:08:44 +1000153 {
David Symonds5ed980c2011-11-29 09:52:21 +1100154 in: `inner: < host: "\0" >` + "\n",
155 err: `line 1.15: invalid quoted string "\0"`,
Rob Pikeaaa3a622010-03-20 22:32:34 -0700156 },
157
158 // Number too large for int64
David Symonds9f402812011-04-28 18:08:44 +1000159 {
David Symonds8bb628d2014-07-22 13:49:35 +1000160 in: "count: 1 others { key: 123456789012345678901 }",
161 err: "line 1.23: invalid int64: 123456789012345678901",
Rob Pikeaaa3a622010-03-20 22:32:34 -0700162 },
163
164 // Number too large for int32
David Symonds9f402812011-04-28 18:08:44 +1000165 {
David Symonds5ed980c2011-11-29 09:52:21 +1100166 in: "count: 1234567890123",
167 err: "line 1.7: invalid int32: 1234567890123",
Rob Pikeaaa3a622010-03-20 22:32:34 -0700168 },
169
David Symonds32612dd2012-06-15 07:59:05 -0700170 // Number in hexadecimal
171 {
172 in: "count: 0x2beef",
173 out: &MyMessage{
174 Count: Int32(0x2beef),
175 },
176 },
177
178 // Number in octal
179 {
180 in: "count: 024601",
181 out: &MyMessage{
182 Count: Int32(024601),
183 },
184 },
185
David Symonds6bd081e2012-06-28 10:46:25 -0700186 // Floating point number with "f" suffix
187 {
188 in: "count: 4 others:< weight: 17.0f >",
189 out: &MyMessage{
190 Count: Int32(4),
191 Others: []*OtherMessage{
192 {
193 Weight: Float32(17),
194 },
195 },
196 },
197 },
198
David Symonds31faaca2012-12-06 15:51:11 +1100199 // Floating point positive infinity
200 {
201 in: "count: 4 bigfloat: inf",
202 out: &MyMessage{
203 Count: Int32(4),
204 Bigfloat: Float64(math.Inf(1)),
205 },
206 },
207
208 // Floating point negative infinity
209 {
210 in: "count: 4 bigfloat: -inf",
211 out: &MyMessage{
212 Count: Int32(4),
213 Bigfloat: Float64(math.Inf(-1)),
214 },
215 },
216
Rob Pikeaaa3a622010-03-20 22:32:34 -0700217 // Number too large for float32
David Symonds9f402812011-04-28 18:08:44 +1000218 {
David Symonds5ed980c2011-11-29 09:52:21 +1100219 in: "others:< weight: 12345678901234567890123456789012345678901234567890 >",
220 err: "line 1.17: invalid float32: 12345678901234567890123456789012345678901234567890",
Rob Pikeaaa3a622010-03-20 22:32:34 -0700221 },
222
223 // Number posing as a quoted string
David Symonds9f402812011-04-28 18:08:44 +1000224 {
David Symonds5ed980c2011-11-29 09:52:21 +1100225 in: `inner: < host: 12 >` + "\n",
226 err: `line 1.15: invalid string: 12`,
Rob Pikeaaa3a622010-03-20 22:32:34 -0700227 },
228
229 // Quoted string posing as int32
David Symonds9f402812011-04-28 18:08:44 +1000230 {
David Symonds5ed980c2011-11-29 09:52:21 +1100231 in: `count: "12"`,
232 err: `line 1.7: invalid int32: "12"`,
Rob Pikeaaa3a622010-03-20 22:32:34 -0700233 },
234
235 // Quoted string posing a float32
David Symonds9f402812011-04-28 18:08:44 +1000236 {
David Symonds5ed980c2011-11-29 09:52:21 +1100237 in: `others:< weight: "17.4" >`,
238 err: `line 1.17: invalid float32: "17.4"`,
Rob Pikeaaa3a622010-03-20 22:32:34 -0700239 },
240
241 // Enum
David Symonds9f402812011-04-28 18:08:44 +1000242 {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700243 in: `count:42 bikeshed: BLUE`,
244 out: &MyMessage{
Rob Pike9caa5b92010-05-11 16:04:57 -0700245 Count: Int32(42),
David Symondsefeca9a2012-05-08 10:36:04 +1000246 Bikeshed: MyMessage_BLUE.Enum(),
Rob Pikeaaa3a622010-03-20 22:32:34 -0700247 },
248 },
249
250 // Repeated field
David Symonds9f402812011-04-28 18:08:44 +1000251 {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700252 in: `count:42 pet: "horsey" pet:"bunny"`,
253 out: &MyMessage{
254 Count: Int32(42),
Rob Pike9caa5b92010-05-11 16:04:57 -0700255 Pet: []string{"horsey", "bunny"},
Rob Pikeaaa3a622010-03-20 22:32:34 -0700256 },
257 },
258
Rob Pikeaaf695a2010-06-22 15:51:21 -0700259 // Repeated message with/without colon and <>/{}
David Symonds9f402812011-04-28 18:08:44 +1000260 {
Rob Pikeaaf695a2010-06-22 15:51:21 -0700261 in: `count:42 others:{} others{} others:<> others:{}`,
262 out: &MyMessage{
263 Count: Int32(42),
264 Others: []*OtherMessage{
Albert Strasheim4676f6a2013-04-07 08:59:06 +1000265 {},
266 {},
267 {},
268 {},
Rob Pikeaaf695a2010-06-22 15:51:21 -0700269 },
270 },
271 },
272
Rob Pikeaaa3a622010-03-20 22:32:34 -0700273 // Missing colon for inner message
David Symonds9f402812011-04-28 18:08:44 +1000274 {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700275 in: `count:42 inner < host: "cauchy.syd" >`,
276 out: &MyMessage{
277 Count: Int32(42),
278 Inner: &InnerMessage{
279 Host: String("cauchy.syd"),
280 },
281 },
282 },
283
284 // Missing colon for string field
David Symonds9f402812011-04-28 18:08:44 +1000285 {
David Symonds5ed980c2011-11-29 09:52:21 +1100286 in: `name "Dave"`,
287 err: `line 1.5: expected ':', found "\"Dave\""`,
Rob Pikeaaa3a622010-03-20 22:32:34 -0700288 },
289
290 // Missing colon for int32 field
David Symonds9f402812011-04-28 18:08:44 +1000291 {
David Symonds5ed980c2011-11-29 09:52:21 +1100292 in: `count 42`,
293 err: `line 1.6: expected ':', found "42"`,
Rob Pikeaaa3a622010-03-20 22:32:34 -0700294 },
295
296 // Missing required field
David Symonds9f402812011-04-28 18:08:44 +1000297 {
David Symonds2a1c6b92014-10-12 16:42:41 +1100298 in: `name: "Pawel"`,
299 err: `proto: required field "testdata.MyMessage.count" not set`,
300 out: &MyMessage{
301 Name: String("Pawel"),
302 },
Rob Pikeaaa3a622010-03-20 22:32:34 -0700303 },
304
305 // Repeated non-repeated field
David Symonds9f402812011-04-28 18:08:44 +1000306 {
David Symonds5ed980c2011-11-29 09:52:21 +1100307 in: `name: "Rob" name: "Russ"`,
308 err: `line 1.12: non-repeated field "name" was repeated`,
Rob Pikeaaa3a622010-03-20 22:32:34 -0700309 },
310
David Symonds9f402812011-04-28 18:08:44 +1000311 // Group
312 {
313 in: `count: 17 SomeGroup { group_field: 12 }`,
314 out: &MyMessage{
315 Count: Int32(17),
316 Somegroup: &MyMessage_SomeGroup{
317 GroupField: Int32(12),
318 },
319 },
320 },
321
David Symonds31faaca2012-12-06 15:51:11 +1100322 // Semicolon between fields
323 {
324 in: `count:3;name:"Calvin"`,
325 out: &MyMessage{
326 Count: Int32(3),
327 Name: String("Calvin"),
328 },
329 },
330 // Comma between fields
331 {
332 in: `count:4,name:"Ezekiel"`,
333 out: &MyMessage{
334 Count: Int32(4),
335 Name: String("Ezekiel"),
336 },
337 },
338
David Symonds54531052011-12-08 12:00:31 +1100339 // Extension
Rob Pikeb7907bf2012-02-13 14:25:20 +1100340 buildExtStructTest(`count: 42 [testdata.Ext.more]:<data:"Hello, world!" >`),
341 buildExtStructTest(`count: 42 [testdata.Ext.more] {data:"Hello, world!"}`),
342 buildExtDataTest(`count: 42 [testdata.Ext.text]:"Hello, world!" [testdata.Ext.number]:1729`),
David Symonds61826da2012-05-05 09:31:28 +1000343 buildExtRepStringTest(`count: 42 [testdata.greeting]:"bula" [testdata.greeting]:"hola"`),
David Symonds54531052011-12-08 12:00:31 +1100344
Rob Pikeaaa3a622010-03-20 22:32:34 -0700345 // Big all-in-one
David Symonds9f402812011-04-28 18:08:44 +1000346 {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700347 in: "count:42 # Meaning\n" +
348 `name:"Dave" ` +
349 `quote:"\"I didn't want to go.\"" ` +
350 `pet:"bunny" ` +
351 `pet:"kitty" ` +
352 `pet:"horsey" ` +
353 `inner:<` +
354 ` host:"footrest.syd" ` +
355 ` port:7001 ` +
356 ` connected:true ` +
357 `> ` +
358 `others:<` +
359 ` key:3735928559 ` +
360 ` value:"\x01A\a\f" ` +
361 `> ` +
362 `others:<` +
363 " weight:58.9 # Atomic weight of Co\n" +
364 ` inner:<` +
365 ` host:"lesha.mtv" ` +
366 ` port:8002 ` +
367 ` >` +
368 `>`,
369 out: &MyMessage{
370 Count: Int32(42),
Rob Pike9caa5b92010-05-11 16:04:57 -0700371 Name: String("Dave"),
Rob Pikeaaa3a622010-03-20 22:32:34 -0700372 Quote: String(`"I didn't want to go."`),
Rob Pike9caa5b92010-05-11 16:04:57 -0700373 Pet: []string{"bunny", "kitty", "horsey"},
Rob Pikeaaa3a622010-03-20 22:32:34 -0700374 Inner: &InnerMessage{
Rob Pike9caa5b92010-05-11 16:04:57 -0700375 Host: String("footrest.syd"),
376 Port: Int32(7001),
Rob Pikeaaa3a622010-03-20 22:32:34 -0700377 Connected: Bool(true),
378 },
379 Others: []*OtherMessage{
Albert Strasheim4676f6a2013-04-07 08:59:06 +1000380 {
Rob Pike9caa5b92010-05-11 16:04:57 -0700381 Key: Int64(3735928559),
Rob Pikeaaa3a622010-03-20 22:32:34 -0700382 Value: []byte{0x1, 'A', '\a', '\f'},
383 },
Albert Strasheim4676f6a2013-04-07 08:59:06 +1000384 {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700385 Weight: Float32(58.9),
386 Inner: &InnerMessage{
387 Host: String("lesha.mtv"),
388 Port: Int32(8002),
389 },
390 },
391 },
392 },
393 },
394}
395
396func TestUnmarshalText(t *testing.T) {
397 for i, test := range unMarshalTextTests {
398 pb := new(MyMessage)
399 err := UnmarshalText(test.in, pb)
David Symonds5ed980c2011-11-29 09:52:21 +1100400 if test.err == "" {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700401 // We don't expect failure.
402 if err != nil {
403 t.Errorf("Test %d: Unexpected error: %v", i, err)
404 } else if !reflect.DeepEqual(pb, test.out) {
David Symondsef8f0e82011-10-13 12:57:34 +1100405 t.Errorf("Test %d: Incorrect populated \nHave: %v\nWant: %v",
406 i, pb, test.out)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700407 }
408 } else {
409 // We do expect failure.
410 if err == nil {
David Symonds5ed980c2011-11-29 09:52:21 +1100411 t.Errorf("Test %d: Didn't get expected error: %v", i, test.err)
412 } else if err.Error() != test.err {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700413 t.Errorf("Test %d: Incorrect error.\nHave: %v\nWant: %v",
David Symonds5ed980c2011-11-29 09:52:21 +1100414 i, err.Error(), test.err)
David Symonds2a1c6b92014-10-12 16:42:41 +1100415 } else if _, ok := err.(*RequiredNotSetError); ok && test.out != nil && !reflect.DeepEqual(pb, test.out) {
416 t.Errorf("Test %d: Incorrect populated \nHave: %v\nWant: %v",
417 i, pb, test.out)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700418 }
419 }
420 }
421}
David Symonds79eae332010-10-16 11:33:20 +1100422
David Symonds267e8052014-02-19 14:50:51 +1100423func TestUnmarshalTextCustomMessage(t *testing.T) {
424 msg := &textMessage{}
425 if err := UnmarshalText("custom", msg); err != nil {
426 t.Errorf("Unexpected error from custom unmarshal: %v", err)
427 }
428 if UnmarshalText("not custom", msg) == nil {
429 t.Errorf("Didn't get expected error from custom unmarshal")
430 }
431}
432
David Symondsef8f0e82011-10-13 12:57:34 +1100433// Regression test; this caused a panic.
434func TestRepeatedEnum(t *testing.T) {
435 pb := new(RepeatedEnum)
436 if err := UnmarshalText("color: RED", pb); err != nil {
437 t.Fatal(err)
438 }
439 exp := &RepeatedEnum{
440 Color: []RepeatedEnum_Color{RepeatedEnum_RED},
441 }
David Symonds007ed9d2012-07-24 10:59:36 +1000442 if !Equal(pb, exp) {
David Symondsef8f0e82011-10-13 12:57:34 +1100443 t.Errorf("Incorrect populated \nHave: %v\nWant: %v", pb, exp)
444 }
445}
446
David Symondsabd3b412014-11-28 11:43:44 +1100447func TestProto3TextParsing(t *testing.T) {
448 m := new(proto3pb.Message)
449 const in = `name: "Wallace" true_scotsman: true`
450 want := &proto3pb.Message{
451 Name: "Wallace",
452 TrueScotsman: true,
453 }
454 if err := UnmarshalText(in, m); err != nil {
455 t.Fatal(err)
456 }
457 if !Equal(m, want) {
458 t.Errorf("\n got %v\nwant %v", m, want)
459 }
460}
461
David Symonds3ea3e052014-12-22 16:15:28 +1100462func TestMapParsing(t *testing.T) {
463 m := new(MessageWithMap)
464 const in = `name_mapping:<key:1234 value:"Feist"> name_mapping:<key:1 value:"Beatles">` +
David Symonds056d5ce2015-05-12 19:27:00 +1000465 `msg_mapping:<key:-4, value:<f: 2.0>,>` + // separating commas are okay
David Symondsa11b6342015-01-28 17:07:47 +1100466 `msg_mapping<key:-2 value<f: 4.0>>` + // no colon after "value"
David Symonds3ea3e052014-12-22 16:15:28 +1100467 `byte_mapping:<key:true value:"so be it">`
468 want := &MessageWithMap{
469 NameMapping: map[int32]string{
470 1: "Beatles",
471 1234: "Feist",
472 },
473 MsgMapping: map[int64]*FloatingPoint{
474 -4: {F: Float64(2.0)},
David Symondsa11b6342015-01-28 17:07:47 +1100475 -2: {F: Float64(4.0)},
David Symonds3ea3e052014-12-22 16:15:28 +1100476 },
477 ByteMapping: map[bool][]byte{
478 true: []byte("so be it"),
479 },
480 }
481 if err := UnmarshalText(in, m); err != nil {
482 t.Fatal(err)
483 }
484 if !Equal(m, want) {
485 t.Errorf("\n got %v\nwant %v", m, want)
486 }
487}
488
David Symonds79eae332010-10-16 11:33:20 +1100489var benchInput string
490
491func init() {
492 benchInput = "count: 4\n"
493 for i := 0; i < 1000; i++ {
494 benchInput += "pet: \"fido\"\n"
495 }
496
497 // Check it is valid input.
498 pb := new(MyMessage)
499 err := UnmarshalText(benchInput, pb)
500 if err != nil {
Rob Pikea17fdd92011-11-02 12:43:05 -0700501 panic("Bad benchmark input: " + err.Error())
David Symonds79eae332010-10-16 11:33:20 +1100502 }
503}
504
505func BenchmarkUnmarshalText(b *testing.B) {
506 pb := new(MyMessage)
507 for i := 0; i < b.N; i++ {
508 UnmarshalText(benchInput, pb)
509 }
510 b.SetBytes(int64(len(benchInput)))
511}