blob: e447ffaf24960b608cb1ee9856fad802762376c7 [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.
Rob Pikeaaa3a622010-03-20 22:32:34 -07004// http://code.google.com/p/goprotobuf/
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
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 Symonds704096f2012-03-15 15:10:26 +110039 . "./testdata"
David Symonds20c73662012-01-20 07:32:21 +110040 . "code.google.com/p/goprotobuf/proto"
Rob Pikeaaa3a622010-03-20 22:32:34 -070041)
42
43type UnmarshalTextTest struct {
David Symonds5ed980c2011-11-29 09:52:21 +110044 in string
45 err string // if "", no error expected
46 out *MyMessage
Rob Pikeaaa3a622010-03-20 22:32:34 -070047}
48
David Symonds54531052011-12-08 12:00:31 +110049func buildExtStructTest(text string) UnmarshalTextTest {
50 msg := &MyMessage{
51 Count: Int32(42),
52 }
53 SetExtension(msg, E_Ext_More, &Ext{
54 Data: String("Hello, world!"),
55 })
56 return UnmarshalTextTest{in: text, out: msg}
57}
58
59func buildExtDataTest(text string) UnmarshalTextTest {
60 msg := &MyMessage{
61 Count: Int32(42),
62 }
63 SetExtension(msg, E_Ext_Text, String("Hello, world!"))
64 SetExtension(msg, E_Ext_Number, Int32(1729))
65 return UnmarshalTextTest{in: text, out: msg}
66}
67
David Symonds61826da2012-05-05 09:31:28 +100068func buildExtRepStringTest(text string) UnmarshalTextTest {
69 msg := &MyMessage{
70 Count: Int32(42),
71 }
72 if err := SetExtension(msg, E_Greeting, []string{"bula", "hola"}); err != nil {
73 panic(err)
74 }
75 return UnmarshalTextTest{in: text, out: msg}
76}
77
Rob Pikeaaa3a622010-03-20 22:32:34 -070078var unMarshalTextTests = []UnmarshalTextTest{
79 // Basic
David Symonds9f402812011-04-28 18:08:44 +100080 {
Rob Pikeaaa3a622010-03-20 22:32:34 -070081 in: " count:42\n name:\"Dave\" ",
82 out: &MyMessage{
83 Count: Int32(42),
Rob Pike9caa5b92010-05-11 16:04:57 -070084 Name: String("Dave"),
Rob Pikeaaa3a622010-03-20 22:32:34 -070085 },
86 },
87
88 // Empty quoted string
David Symonds9f402812011-04-28 18:08:44 +100089 {
Rob Pikeaaa3a622010-03-20 22:32:34 -070090 in: `count:42 name:""`,
91 out: &MyMessage{
92 Count: Int32(42),
Rob Pike9caa5b92010-05-11 16:04:57 -070093 Name: String(""),
Rob Pikeaaa3a622010-03-20 22:32:34 -070094 },
95 },
96
97 // Quoted string concatenation
David Symonds9f402812011-04-28 18:08:44 +100098 {
Rob Pikeaaa3a622010-03-20 22:32:34 -070099 in: `count:42 name: "My name is "` + "\n" + `"elsewhere"`,
100 out: &MyMessage{
101 Count: Int32(42),
Rob Pike9caa5b92010-05-11 16:04:57 -0700102 Name: String("My name is elsewhere"),
Rob Pikeaaa3a622010-03-20 22:32:34 -0700103 },
104 },
105
David Symonds183124e2012-03-23 13:20:23 +1100106 // Quoted string with escaped apostrophe
107 {
108 in: `count:42 name: "HOLIDAY - New Year\'s Day"`,
109 out: &MyMessage{
110 Count: Int32(42),
111 Name: String("HOLIDAY - New Year's Day"),
112 },
113 },
114
David Symonds162d0032012-06-28 09:44:46 -0700115 // Quoted string with single quote
116 {
117 in: `count:42 name: 'Roger "The Ramster" Ramjet'`,
118 out: &MyMessage{
119 Count: Int32(42),
120 Name: String(`Roger "The Ramster" Ramjet`),
121 },
122 },
123
David Symondsfa94a1e2012-09-24 13:21:49 +1000124 // Quoted string with all the accepted special characters from the C++ test
125 {
126 in: `count:42 name: ` + "\"\\\"A string with \\' characters \\n and \\r newlines and \\t tabs and \\001 slashes \\\\ and multiple spaces\"",
127 out: &MyMessage{
128 Count: Int32(42),
129 Name: String("\"A string with ' characters \n and \r newlines and \t tabs and \001 slashes \\ and multiple spaces"),
130 },
131 },
132
133 // Quoted string with quoted backslash
134 {
135 in: `count:42 name: "\\'xyz"`,
136 out: &MyMessage{
137 Count: Int32(42),
138 Name: String(`\'xyz`),
139 },
140 },
141
142 // Quoted string with UTF-8 bytes.
143 {
144 in: "count:42 name: '\303\277\302\201\xAB'",
145 out: &MyMessage{
146 Count: Int32(42),
147 Name: String("\303\277\302\201\xAB"),
148 },
149 },
150
Rob Pikeaaa3a622010-03-20 22:32:34 -0700151 // Bad quoted string
David Symonds9f402812011-04-28 18:08:44 +1000152 {
David Symonds5ed980c2011-11-29 09:52:21 +1100153 in: `inner: < host: "\0" >` + "\n",
154 err: `line 1.15: invalid quoted string "\0"`,
Rob Pikeaaa3a622010-03-20 22:32:34 -0700155 },
156
157 // Number too large for int64
David Symonds9f402812011-04-28 18:08:44 +1000158 {
David Symonds5ed980c2011-11-29 09:52:21 +1100159 in: "count: 123456789012345678901",
160 err: "line 1.7: invalid int32: 123456789012345678901",
Rob Pikeaaa3a622010-03-20 22:32:34 -0700161 },
162
163 // Number too large for int32
David Symonds9f402812011-04-28 18:08:44 +1000164 {
David Symonds5ed980c2011-11-29 09:52:21 +1100165 in: "count: 1234567890123",
166 err: "line 1.7: invalid int32: 1234567890123",
Rob Pikeaaa3a622010-03-20 22:32:34 -0700167 },
168
David Symonds32612dd2012-06-15 07:59:05 -0700169 // Number in hexadecimal
170 {
171 in: "count: 0x2beef",
172 out: &MyMessage{
173 Count: Int32(0x2beef),
174 },
175 },
176
177 // Number in octal
178 {
179 in: "count: 024601",
180 out: &MyMessage{
181 Count: Int32(024601),
182 },
183 },
184
David Symonds6bd081e2012-06-28 10:46:25 -0700185 // Floating point number with "f" suffix
186 {
187 in: "count: 4 others:< weight: 17.0f >",
188 out: &MyMessage{
189 Count: Int32(4),
190 Others: []*OtherMessage{
191 {
192 Weight: Float32(17),
193 },
194 },
195 },
196 },
197
David Symonds31faaca2012-12-06 15:51:11 +1100198 // Floating point positive infinity
199 {
200 in: "count: 4 bigfloat: inf",
201 out: &MyMessage{
202 Count: Int32(4),
203 Bigfloat: Float64(math.Inf(1)),
204 },
205 },
206
207 // Floating point negative infinity
208 {
209 in: "count: 4 bigfloat: -inf",
210 out: &MyMessage{
211 Count: Int32(4),
212 Bigfloat: Float64(math.Inf(-1)),
213 },
214 },
215
Rob Pikeaaa3a622010-03-20 22:32:34 -0700216 // Number too large for float32
David Symonds9f402812011-04-28 18:08:44 +1000217 {
David Symonds5ed980c2011-11-29 09:52:21 +1100218 in: "others:< weight: 12345678901234567890123456789012345678901234567890 >",
219 err: "line 1.17: invalid float32: 12345678901234567890123456789012345678901234567890",
Rob Pikeaaa3a622010-03-20 22:32:34 -0700220 },
221
222 // Number posing as a quoted string
David Symonds9f402812011-04-28 18:08:44 +1000223 {
David Symonds5ed980c2011-11-29 09:52:21 +1100224 in: `inner: < host: 12 >` + "\n",
225 err: `line 1.15: invalid string: 12`,
Rob Pikeaaa3a622010-03-20 22:32:34 -0700226 },
227
228 // Quoted string posing as int32
David Symonds9f402812011-04-28 18:08:44 +1000229 {
David Symonds5ed980c2011-11-29 09:52:21 +1100230 in: `count: "12"`,
231 err: `line 1.7: invalid int32: "12"`,
Rob Pikeaaa3a622010-03-20 22:32:34 -0700232 },
233
234 // Quoted string posing a float32
David Symonds9f402812011-04-28 18:08:44 +1000235 {
David Symonds5ed980c2011-11-29 09:52:21 +1100236 in: `others:< weight: "17.4" >`,
237 err: `line 1.17: invalid float32: "17.4"`,
Rob Pikeaaa3a622010-03-20 22:32:34 -0700238 },
239
240 // Enum
David Symonds9f402812011-04-28 18:08:44 +1000241 {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700242 in: `count:42 bikeshed: BLUE`,
243 out: &MyMessage{
Rob Pike9caa5b92010-05-11 16:04:57 -0700244 Count: Int32(42),
David Symondsefeca9a2012-05-08 10:36:04 +1000245 Bikeshed: MyMessage_BLUE.Enum(),
Rob Pikeaaa3a622010-03-20 22:32:34 -0700246 },
247 },
248
249 // Repeated field
David Symonds9f402812011-04-28 18:08:44 +1000250 {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700251 in: `count:42 pet: "horsey" pet:"bunny"`,
252 out: &MyMessage{
253 Count: Int32(42),
Rob Pike9caa5b92010-05-11 16:04:57 -0700254 Pet: []string{"horsey", "bunny"},
Rob Pikeaaa3a622010-03-20 22:32:34 -0700255 },
256 },
257
Rob Pikeaaf695a2010-06-22 15:51:21 -0700258 // Repeated message with/without colon and <>/{}
David Symonds9f402812011-04-28 18:08:44 +1000259 {
Rob Pikeaaf695a2010-06-22 15:51:21 -0700260 in: `count:42 others:{} others{} others:<> others:{}`,
261 out: &MyMessage{
262 Count: Int32(42),
263 Others: []*OtherMessage{
Albert Strasheim4676f6a2013-04-07 08:59:06 +1000264 {},
265 {},
266 {},
267 {},
Rob Pikeaaf695a2010-06-22 15:51:21 -0700268 },
269 },
270 },
271
Rob Pikeaaa3a622010-03-20 22:32:34 -0700272 // Missing colon for inner message
David Symonds9f402812011-04-28 18:08:44 +1000273 {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700274 in: `count:42 inner < host: "cauchy.syd" >`,
275 out: &MyMessage{
276 Count: Int32(42),
277 Inner: &InnerMessage{
278 Host: String("cauchy.syd"),
279 },
280 },
281 },
282
283 // Missing colon for string field
David Symonds9f402812011-04-28 18:08:44 +1000284 {
David Symonds5ed980c2011-11-29 09:52:21 +1100285 in: `name "Dave"`,
286 err: `line 1.5: expected ':', found "\"Dave\""`,
Rob Pikeaaa3a622010-03-20 22:32:34 -0700287 },
288
289 // Missing colon for int32 field
David Symonds9f402812011-04-28 18:08:44 +1000290 {
David Symonds5ed980c2011-11-29 09:52:21 +1100291 in: `count 42`,
292 err: `line 1.6: expected ':', found "42"`,
Rob Pikeaaa3a622010-03-20 22:32:34 -0700293 },
294
295 // Missing required field
David Symonds9f402812011-04-28 18:08:44 +1000296 {
David Symonds5ed980c2011-11-29 09:52:21 +1100297 in: ``,
Rob Pikeb7907bf2012-02-13 14:25:20 +1100298 err: `line 1.0: message testdata.MyMessage missing required field "count"`,
Rob Pikeaaa3a622010-03-20 22:32:34 -0700299 },
300
301 // Repeated non-repeated field
David Symonds9f402812011-04-28 18:08:44 +1000302 {
David Symonds5ed980c2011-11-29 09:52:21 +1100303 in: `name: "Rob" name: "Russ"`,
304 err: `line 1.12: non-repeated field "name" was repeated`,
Rob Pikeaaa3a622010-03-20 22:32:34 -0700305 },
306
David Symonds9f402812011-04-28 18:08:44 +1000307 // Group
308 {
309 in: `count: 17 SomeGroup { group_field: 12 }`,
310 out: &MyMessage{
311 Count: Int32(17),
312 Somegroup: &MyMessage_SomeGroup{
313 GroupField: Int32(12),
314 },
315 },
316 },
317
David Symonds31faaca2012-12-06 15:51:11 +1100318 // Semicolon between fields
319 {
320 in: `count:3;name:"Calvin"`,
321 out: &MyMessage{
322 Count: Int32(3),
323 Name: String("Calvin"),
324 },
325 },
326 // Comma between fields
327 {
328 in: `count:4,name:"Ezekiel"`,
329 out: &MyMessage{
330 Count: Int32(4),
331 Name: String("Ezekiel"),
332 },
333 },
334
David Symonds54531052011-12-08 12:00:31 +1100335 // Extension
Rob Pikeb7907bf2012-02-13 14:25:20 +1100336 buildExtStructTest(`count: 42 [testdata.Ext.more]:<data:"Hello, world!" >`),
337 buildExtStructTest(`count: 42 [testdata.Ext.more] {data:"Hello, world!"}`),
338 buildExtDataTest(`count: 42 [testdata.Ext.text]:"Hello, world!" [testdata.Ext.number]:1729`),
David Symonds61826da2012-05-05 09:31:28 +1000339 buildExtRepStringTest(`count: 42 [testdata.greeting]:"bula" [testdata.greeting]:"hola"`),
David Symonds54531052011-12-08 12:00:31 +1100340
Rob Pikeaaa3a622010-03-20 22:32:34 -0700341 // Big all-in-one
David Symonds9f402812011-04-28 18:08:44 +1000342 {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700343 in: "count:42 # Meaning\n" +
344 `name:"Dave" ` +
345 `quote:"\"I didn't want to go.\"" ` +
346 `pet:"bunny" ` +
347 `pet:"kitty" ` +
348 `pet:"horsey" ` +
349 `inner:<` +
350 ` host:"footrest.syd" ` +
351 ` port:7001 ` +
352 ` connected:true ` +
353 `> ` +
354 `others:<` +
355 ` key:3735928559 ` +
356 ` value:"\x01A\a\f" ` +
357 `> ` +
358 `others:<` +
359 " weight:58.9 # Atomic weight of Co\n" +
360 ` inner:<` +
361 ` host:"lesha.mtv" ` +
362 ` port:8002 ` +
363 ` >` +
364 `>`,
365 out: &MyMessage{
366 Count: Int32(42),
Rob Pike9caa5b92010-05-11 16:04:57 -0700367 Name: String("Dave"),
Rob Pikeaaa3a622010-03-20 22:32:34 -0700368 Quote: String(`"I didn't want to go."`),
Rob Pike9caa5b92010-05-11 16:04:57 -0700369 Pet: []string{"bunny", "kitty", "horsey"},
Rob Pikeaaa3a622010-03-20 22:32:34 -0700370 Inner: &InnerMessage{
Rob Pike9caa5b92010-05-11 16:04:57 -0700371 Host: String("footrest.syd"),
372 Port: Int32(7001),
Rob Pikeaaa3a622010-03-20 22:32:34 -0700373 Connected: Bool(true),
374 },
375 Others: []*OtherMessage{
Albert Strasheim4676f6a2013-04-07 08:59:06 +1000376 {
Rob Pike9caa5b92010-05-11 16:04:57 -0700377 Key: Int64(3735928559),
Rob Pikeaaa3a622010-03-20 22:32:34 -0700378 Value: []byte{0x1, 'A', '\a', '\f'},
379 },
Albert Strasheim4676f6a2013-04-07 08:59:06 +1000380 {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700381 Weight: Float32(58.9),
382 Inner: &InnerMessage{
383 Host: String("lesha.mtv"),
384 Port: Int32(8002),
385 },
386 },
387 },
388 },
389 },
390}
391
392func TestUnmarshalText(t *testing.T) {
393 for i, test := range unMarshalTextTests {
394 pb := new(MyMessage)
395 err := UnmarshalText(test.in, pb)
David Symonds5ed980c2011-11-29 09:52:21 +1100396 if test.err == "" {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700397 // We don't expect failure.
398 if err != nil {
399 t.Errorf("Test %d: Unexpected error: %v", i, err)
400 } else if !reflect.DeepEqual(pb, test.out) {
David Symondsef8f0e82011-10-13 12:57:34 +1100401 t.Errorf("Test %d: Incorrect populated \nHave: %v\nWant: %v",
402 i, pb, test.out)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700403 }
404 } else {
405 // We do expect failure.
406 if err == nil {
David Symonds5ed980c2011-11-29 09:52:21 +1100407 t.Errorf("Test %d: Didn't get expected error: %v", i, test.err)
408 } else if err.Error() != test.err {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700409 t.Errorf("Test %d: Incorrect error.\nHave: %v\nWant: %v",
David Symonds5ed980c2011-11-29 09:52:21 +1100410 i, err.Error(), test.err)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700411 }
412 }
413 }
414}
David Symonds79eae332010-10-16 11:33:20 +1100415
David Symonds267e8052014-02-19 14:50:51 +1100416func TestUnmarshalTextCustomMessage(t *testing.T) {
417 msg := &textMessage{}
418 if err := UnmarshalText("custom", msg); err != nil {
419 t.Errorf("Unexpected error from custom unmarshal: %v", err)
420 }
421 if UnmarshalText("not custom", msg) == nil {
422 t.Errorf("Didn't get expected error from custom unmarshal")
423 }
424}
425
David Symondsef8f0e82011-10-13 12:57:34 +1100426// Regression test; this caused a panic.
427func TestRepeatedEnum(t *testing.T) {
428 pb := new(RepeatedEnum)
429 if err := UnmarshalText("color: RED", pb); err != nil {
430 t.Fatal(err)
431 }
432 exp := &RepeatedEnum{
433 Color: []RepeatedEnum_Color{RepeatedEnum_RED},
434 }
David Symonds007ed9d2012-07-24 10:59:36 +1000435 if !Equal(pb, exp) {
David Symondsef8f0e82011-10-13 12:57:34 +1100436 t.Errorf("Incorrect populated \nHave: %v\nWant: %v", pb, exp)
437 }
438}
439
David Symonds79eae332010-10-16 11:33:20 +1100440var benchInput string
441
442func init() {
443 benchInput = "count: 4\n"
444 for i := 0; i < 1000; i++ {
445 benchInput += "pet: \"fido\"\n"
446 }
447
448 // Check it is valid input.
449 pb := new(MyMessage)
450 err := UnmarshalText(benchInput, pb)
451 if err != nil {
Rob Pikea17fdd92011-11-02 12:43:05 -0700452 panic("Bad benchmark input: " + err.Error())
David Symonds79eae332010-10-16 11:33:20 +1100453 }
454}
455
456func BenchmarkUnmarshalText(b *testing.B) {
457 pb := new(MyMessage)
458 for i := 0; i < b.N; i++ {
459 UnmarshalText(benchInput, pb)
460 }
461 b.SetBytes(int64(len(benchInput)))
462}