blob: ce058f4bf1689a425e1c8d33c04ca2f4a0169268 [file] [log] [blame]
Rob Pikeaaa3a622010-03-20 22:32:34 -07001// Go support for Protocol Buffers - Google's data interchange format
2//
3// Copyright 2010 Google Inc. All rights reserved.
4// 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 (
Rob Pikeaaa3a622010-03-20 22:32:34 -070035 "reflect"
36 "testing"
David Symonds20c73662012-01-20 07:32:21 +110037
David Symonds704096f2012-03-15 15:10:26 +110038 . "./testdata"
David Symonds20c73662012-01-20 07:32:21 +110039 . "code.google.com/p/goprotobuf/proto"
Rob Pikeaaa3a622010-03-20 22:32:34 -070040)
41
42type UnmarshalTextTest struct {
David Symonds5ed980c2011-11-29 09:52:21 +110043 in string
44 err string // if "", no error expected
45 out *MyMessage
Rob Pikeaaa3a622010-03-20 22:32:34 -070046}
47
David Symonds54531052011-12-08 12:00:31 +110048func buildExtStructTest(text string) UnmarshalTextTest {
49 msg := &MyMessage{
50 Count: Int32(42),
51 }
52 SetExtension(msg, E_Ext_More, &Ext{
53 Data: String("Hello, world!"),
54 })
55 return UnmarshalTextTest{in: text, out: msg}
56}
57
58func buildExtDataTest(text string) UnmarshalTextTest {
59 msg := &MyMessage{
60 Count: Int32(42),
61 }
62 SetExtension(msg, E_Ext_Text, String("Hello, world!"))
63 SetExtension(msg, E_Ext_Number, Int32(1729))
64 return UnmarshalTextTest{in: text, out: msg}
65}
66
David Symonds61826da2012-05-05 09:31:28 +100067func buildExtRepStringTest(text string) UnmarshalTextTest {
68 msg := &MyMessage{
69 Count: Int32(42),
70 }
71 if err := SetExtension(msg, E_Greeting, []string{"bula", "hola"}); err != nil {
72 panic(err)
73 }
74 return UnmarshalTextTest{in: text, out: msg}
75}
76
Rob Pikeaaa3a622010-03-20 22:32:34 -070077var unMarshalTextTests = []UnmarshalTextTest{
78 // Basic
David Symonds9f402812011-04-28 18:08:44 +100079 {
Rob Pikeaaa3a622010-03-20 22:32:34 -070080 in: " count:42\n name:\"Dave\" ",
81 out: &MyMessage{
82 Count: Int32(42),
Rob Pike9caa5b92010-05-11 16:04:57 -070083 Name: String("Dave"),
Rob Pikeaaa3a622010-03-20 22:32:34 -070084 },
85 },
86
87 // Empty quoted string
David Symonds9f402812011-04-28 18:08:44 +100088 {
Rob Pikeaaa3a622010-03-20 22:32:34 -070089 in: `count:42 name:""`,
90 out: &MyMessage{
91 Count: Int32(42),
Rob Pike9caa5b92010-05-11 16:04:57 -070092 Name: String(""),
Rob Pikeaaa3a622010-03-20 22:32:34 -070093 },
94 },
95
96 // Quoted string concatenation
David Symonds9f402812011-04-28 18:08:44 +100097 {
Rob Pikeaaa3a622010-03-20 22:32:34 -070098 in: `count:42 name: "My name is "` + "\n" + `"elsewhere"`,
99 out: &MyMessage{
100 Count: Int32(42),
Rob Pike9caa5b92010-05-11 16:04:57 -0700101 Name: String("My name is elsewhere"),
Rob Pikeaaa3a622010-03-20 22:32:34 -0700102 },
103 },
104
David Symonds183124e2012-03-23 13:20:23 +1100105 // Quoted string with escaped apostrophe
106 {
107 in: `count:42 name: "HOLIDAY - New Year\'s Day"`,
108 out: &MyMessage{
109 Count: Int32(42),
110 Name: String("HOLIDAY - New Year's Day"),
111 },
112 },
113
David Symonds162d0032012-06-28 09:44:46 -0700114 // Quoted string with single quote
115 {
116 in: `count:42 name: 'Roger "The Ramster" Ramjet'`,
117 out: &MyMessage{
118 Count: Int32(42),
119 Name: String(`Roger "The Ramster" Ramjet`),
120 },
121 },
122
David Symondsfa94a1e2012-09-24 13:21:49 +1000123 // Quoted string with all the accepted special characters from the C++ test
124 {
125 in: `count:42 name: ` + "\"\\\"A string with \\' characters \\n and \\r newlines and \\t tabs and \\001 slashes \\\\ and multiple spaces\"",
126 out: &MyMessage{
127 Count: Int32(42),
128 Name: String("\"A string with ' characters \n and \r newlines and \t tabs and \001 slashes \\ and multiple spaces"),
129 },
130 },
131
132 // Quoted string with quoted backslash
133 {
134 in: `count:42 name: "\\'xyz"`,
135 out: &MyMessage{
136 Count: Int32(42),
137 Name: String(`\'xyz`),
138 },
139 },
140
141 // Quoted string with UTF-8 bytes.
142 {
143 in: "count:42 name: '\303\277\302\201\xAB'",
144 out: &MyMessage{
145 Count: Int32(42),
146 Name: String("\303\277\302\201\xAB"),
147 },
148 },
149
Rob Pikeaaa3a622010-03-20 22:32:34 -0700150 // Bad quoted string
David Symonds9f402812011-04-28 18:08:44 +1000151 {
David Symonds5ed980c2011-11-29 09:52:21 +1100152 in: `inner: < host: "\0" >` + "\n",
153 err: `line 1.15: invalid quoted string "\0"`,
Rob Pikeaaa3a622010-03-20 22:32:34 -0700154 },
155
156 // Number too large for int64
David Symonds9f402812011-04-28 18:08:44 +1000157 {
David Symonds5ed980c2011-11-29 09:52:21 +1100158 in: "count: 123456789012345678901",
159 err: "line 1.7: invalid int32: 123456789012345678901",
Rob Pikeaaa3a622010-03-20 22:32:34 -0700160 },
161
162 // Number too large for int32
David Symonds9f402812011-04-28 18:08:44 +1000163 {
David Symonds5ed980c2011-11-29 09:52:21 +1100164 in: "count: 1234567890123",
165 err: "line 1.7: invalid int32: 1234567890123",
Rob Pikeaaa3a622010-03-20 22:32:34 -0700166 },
167
David Symonds32612dd2012-06-15 07:59:05 -0700168 // Number in hexadecimal
169 {
170 in: "count: 0x2beef",
171 out: &MyMessage{
172 Count: Int32(0x2beef),
173 },
174 },
175
176 // Number in octal
177 {
178 in: "count: 024601",
179 out: &MyMessage{
180 Count: Int32(024601),
181 },
182 },
183
David Symonds6bd081e2012-06-28 10:46:25 -0700184 // Floating point number with "f" suffix
185 {
186 in: "count: 4 others:< weight: 17.0f >",
187 out: &MyMessage{
188 Count: Int32(4),
189 Others: []*OtherMessage{
190 {
191 Weight: Float32(17),
192 },
193 },
194 },
195 },
196
Rob Pikeaaa3a622010-03-20 22:32:34 -0700197 // Number too large for float32
David Symonds9f402812011-04-28 18:08:44 +1000198 {
David Symonds5ed980c2011-11-29 09:52:21 +1100199 in: "others:< weight: 12345678901234567890123456789012345678901234567890 >",
200 err: "line 1.17: invalid float32: 12345678901234567890123456789012345678901234567890",
Rob Pikeaaa3a622010-03-20 22:32:34 -0700201 },
202
203 // Number posing as a quoted string
David Symonds9f402812011-04-28 18:08:44 +1000204 {
David Symonds5ed980c2011-11-29 09:52:21 +1100205 in: `inner: < host: 12 >` + "\n",
206 err: `line 1.15: invalid string: 12`,
Rob Pikeaaa3a622010-03-20 22:32:34 -0700207 },
208
209 // Quoted string posing as int32
David Symonds9f402812011-04-28 18:08:44 +1000210 {
David Symonds5ed980c2011-11-29 09:52:21 +1100211 in: `count: "12"`,
212 err: `line 1.7: invalid int32: "12"`,
Rob Pikeaaa3a622010-03-20 22:32:34 -0700213 },
214
215 // Quoted string posing a float32
David Symonds9f402812011-04-28 18:08:44 +1000216 {
David Symonds5ed980c2011-11-29 09:52:21 +1100217 in: `others:< weight: "17.4" >`,
218 err: `line 1.17: invalid float32: "17.4"`,
Rob Pikeaaa3a622010-03-20 22:32:34 -0700219 },
220
221 // Enum
David Symonds9f402812011-04-28 18:08:44 +1000222 {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700223 in: `count:42 bikeshed: BLUE`,
224 out: &MyMessage{
Rob Pike9caa5b92010-05-11 16:04:57 -0700225 Count: Int32(42),
David Symondsefeca9a2012-05-08 10:36:04 +1000226 Bikeshed: MyMessage_BLUE.Enum(),
Rob Pikeaaa3a622010-03-20 22:32:34 -0700227 },
228 },
229
230 // Repeated field
David Symonds9f402812011-04-28 18:08:44 +1000231 {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700232 in: `count:42 pet: "horsey" pet:"bunny"`,
233 out: &MyMessage{
234 Count: Int32(42),
Rob Pike9caa5b92010-05-11 16:04:57 -0700235 Pet: []string{"horsey", "bunny"},
Rob Pikeaaa3a622010-03-20 22:32:34 -0700236 },
237 },
238
Rob Pikeaaf695a2010-06-22 15:51:21 -0700239 // Repeated message with/without colon and <>/{}
David Symonds9f402812011-04-28 18:08:44 +1000240 {
Rob Pikeaaf695a2010-06-22 15:51:21 -0700241 in: `count:42 others:{} others{} others:<> others:{}`,
242 out: &MyMessage{
243 Count: Int32(42),
244 Others: []*OtherMessage{
245 &OtherMessage{},
246 &OtherMessage{},
247 &OtherMessage{},
248 &OtherMessage{},
249 },
250 },
251 },
252
Rob Pikeaaa3a622010-03-20 22:32:34 -0700253 // Missing colon for inner message
David Symonds9f402812011-04-28 18:08:44 +1000254 {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700255 in: `count:42 inner < host: "cauchy.syd" >`,
256 out: &MyMessage{
257 Count: Int32(42),
258 Inner: &InnerMessage{
259 Host: String("cauchy.syd"),
260 },
261 },
262 },
263
264 // Missing colon for string field
David Symonds9f402812011-04-28 18:08:44 +1000265 {
David Symonds5ed980c2011-11-29 09:52:21 +1100266 in: `name "Dave"`,
267 err: `line 1.5: expected ':', found "\"Dave\""`,
Rob Pikeaaa3a622010-03-20 22:32:34 -0700268 },
269
270 // Missing colon for int32 field
David Symonds9f402812011-04-28 18:08:44 +1000271 {
David Symonds5ed980c2011-11-29 09:52:21 +1100272 in: `count 42`,
273 err: `line 1.6: expected ':', found "42"`,
Rob Pikeaaa3a622010-03-20 22:32:34 -0700274 },
275
276 // Missing required field
David Symonds9f402812011-04-28 18:08:44 +1000277 {
David Symonds5ed980c2011-11-29 09:52:21 +1100278 in: ``,
Rob Pikeb7907bf2012-02-13 14:25:20 +1100279 err: `line 1.0: message testdata.MyMessage missing required field "count"`,
Rob Pikeaaa3a622010-03-20 22:32:34 -0700280 },
281
282 // Repeated non-repeated field
David Symonds9f402812011-04-28 18:08:44 +1000283 {
David Symonds5ed980c2011-11-29 09:52:21 +1100284 in: `name: "Rob" name: "Russ"`,
285 err: `line 1.12: non-repeated field "name" was repeated`,
Rob Pikeaaa3a622010-03-20 22:32:34 -0700286 },
287
David Symonds9f402812011-04-28 18:08:44 +1000288 // Group
289 {
290 in: `count: 17 SomeGroup { group_field: 12 }`,
291 out: &MyMessage{
292 Count: Int32(17),
293 Somegroup: &MyMessage_SomeGroup{
294 GroupField: Int32(12),
295 },
296 },
297 },
298
David Symonds54531052011-12-08 12:00:31 +1100299 // Extension
Rob Pikeb7907bf2012-02-13 14:25:20 +1100300 buildExtStructTest(`count: 42 [testdata.Ext.more]:<data:"Hello, world!" >`),
301 buildExtStructTest(`count: 42 [testdata.Ext.more] {data:"Hello, world!"}`),
302 buildExtDataTest(`count: 42 [testdata.Ext.text]:"Hello, world!" [testdata.Ext.number]:1729`),
David Symonds61826da2012-05-05 09:31:28 +1000303 buildExtRepStringTest(`count: 42 [testdata.greeting]:"bula" [testdata.greeting]:"hola"`),
David Symonds54531052011-12-08 12:00:31 +1100304
Rob Pikeaaa3a622010-03-20 22:32:34 -0700305 // Big all-in-one
David Symonds9f402812011-04-28 18:08:44 +1000306 {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700307 in: "count:42 # Meaning\n" +
308 `name:"Dave" ` +
309 `quote:"\"I didn't want to go.\"" ` +
310 `pet:"bunny" ` +
311 `pet:"kitty" ` +
312 `pet:"horsey" ` +
313 `inner:<` +
314 ` host:"footrest.syd" ` +
315 ` port:7001 ` +
316 ` connected:true ` +
317 `> ` +
318 `others:<` +
319 ` key:3735928559 ` +
320 ` value:"\x01A\a\f" ` +
321 `> ` +
322 `others:<` +
323 " weight:58.9 # Atomic weight of Co\n" +
324 ` inner:<` +
325 ` host:"lesha.mtv" ` +
326 ` port:8002 ` +
327 ` >` +
328 `>`,
329 out: &MyMessage{
330 Count: Int32(42),
Rob Pike9caa5b92010-05-11 16:04:57 -0700331 Name: String("Dave"),
Rob Pikeaaa3a622010-03-20 22:32:34 -0700332 Quote: String(`"I didn't want to go."`),
Rob Pike9caa5b92010-05-11 16:04:57 -0700333 Pet: []string{"bunny", "kitty", "horsey"},
Rob Pikeaaa3a622010-03-20 22:32:34 -0700334 Inner: &InnerMessage{
Rob Pike9caa5b92010-05-11 16:04:57 -0700335 Host: String("footrest.syd"),
336 Port: Int32(7001),
Rob Pikeaaa3a622010-03-20 22:32:34 -0700337 Connected: Bool(true),
338 },
339 Others: []*OtherMessage{
340 &OtherMessage{
Rob Pike9caa5b92010-05-11 16:04:57 -0700341 Key: Int64(3735928559),
Rob Pikeaaa3a622010-03-20 22:32:34 -0700342 Value: []byte{0x1, 'A', '\a', '\f'},
343 },
344 &OtherMessage{
345 Weight: Float32(58.9),
346 Inner: &InnerMessage{
347 Host: String("lesha.mtv"),
348 Port: Int32(8002),
349 },
350 },
351 },
352 },
353 },
354}
355
356func TestUnmarshalText(t *testing.T) {
357 for i, test := range unMarshalTextTests {
358 pb := new(MyMessage)
359 err := UnmarshalText(test.in, pb)
David Symonds5ed980c2011-11-29 09:52:21 +1100360 if test.err == "" {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700361 // We don't expect failure.
362 if err != nil {
363 t.Errorf("Test %d: Unexpected error: %v", i, err)
364 } else if !reflect.DeepEqual(pb, test.out) {
David Symondsef8f0e82011-10-13 12:57:34 +1100365 t.Errorf("Test %d: Incorrect populated \nHave: %v\nWant: %v",
366 i, pb, test.out)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700367 }
368 } else {
369 // We do expect failure.
370 if err == nil {
David Symonds5ed980c2011-11-29 09:52:21 +1100371 t.Errorf("Test %d: Didn't get expected error: %v", i, test.err)
372 } else if err.Error() != test.err {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700373 t.Errorf("Test %d: Incorrect error.\nHave: %v\nWant: %v",
David Symonds5ed980c2011-11-29 09:52:21 +1100374 i, err.Error(), test.err)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700375 }
376 }
377 }
378}
David Symonds79eae332010-10-16 11:33:20 +1100379
David Symondsef8f0e82011-10-13 12:57:34 +1100380// Regression test; this caused a panic.
381func TestRepeatedEnum(t *testing.T) {
382 pb := new(RepeatedEnum)
383 if err := UnmarshalText("color: RED", pb); err != nil {
384 t.Fatal(err)
385 }
386 exp := &RepeatedEnum{
387 Color: []RepeatedEnum_Color{RepeatedEnum_RED},
388 }
David Symonds007ed9d2012-07-24 10:59:36 +1000389 if !Equal(pb, exp) {
David Symondsef8f0e82011-10-13 12:57:34 +1100390 t.Errorf("Incorrect populated \nHave: %v\nWant: %v", pb, exp)
391 }
392}
393
David Symonds79eae332010-10-16 11:33:20 +1100394var benchInput string
395
396func init() {
397 benchInput = "count: 4\n"
398 for i := 0; i < 1000; i++ {
399 benchInput += "pet: \"fido\"\n"
400 }
401
402 // Check it is valid input.
403 pb := new(MyMessage)
404 err := UnmarshalText(benchInput, pb)
405 if err != nil {
Rob Pikea17fdd92011-11-02 12:43:05 -0700406 panic("Bad benchmark input: " + err.Error())
David Symonds79eae332010-10-16 11:33:20 +1100407 }
408}
409
410func BenchmarkUnmarshalText(b *testing.B) {
411 pb := new(MyMessage)
412 for i := 0; i < b.N; i++ {
413 UnmarshalText(benchInput, pb)
414 }
415 b.SetBytes(int64(len(benchInput)))
416}