1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 package proto_test
33
34 import (
35 "bytes"
36 "errors"
37 "io/ioutil"
38 "math"
39 "strings"
40 "sync"
41 "testing"
42
43 "github.com/gogo/protobuf/proto"
44
45 proto3pb "github.com/gogo/protobuf/proto/proto3_proto"
46 pb "github.com/gogo/protobuf/proto/test_proto"
47 "github.com/gogo/protobuf/types"
48 )
49
50
51
52 type textMessage struct {
53 }
54
55 func (*textMessage) MarshalText() ([]byte, error) {
56 return []byte("custom"), nil
57 }
58
59 func (*textMessage) UnmarshalText(bytes []byte) error {
60 if string(bytes) != "custom" {
61 return errors.New("expected 'custom'")
62 }
63 return nil
64 }
65
66 func (*textMessage) Reset() {}
67 func (*textMessage) String() string { return "" }
68 func (*textMessage) ProtoMessage() {}
69
70 func newTestMessage() *pb.MyMessage {
71 msg := &pb.MyMessage{
72 Count: proto.Int32(42),
73 Name: proto.String("Dave"),
74 Quote: proto.String(`"I didn't want to go."`),
75 Pet: []string{"bunny", "kitty", "horsey"},
76 Inner: &pb.InnerMessage{
77 Host: proto.String("footrest.syd"),
78 Port: proto.Int32(7001),
79 Connected: proto.Bool(true),
80 },
81 Others: []*pb.OtherMessage{
82 {
83 Key: proto.Int64(0xdeadbeef),
84 Value: []byte{1, 65, 7, 12},
85 },
86 {
87 Weight: proto.Float32(6.022),
88 Inner: &pb.InnerMessage{
89 Host: proto.String("lesha.mtv"),
90 Port: proto.Int32(8002),
91 },
92 },
93 },
94 Bikeshed: pb.MyMessage_BLUE.Enum(),
95 Somegroup: &pb.MyMessage_SomeGroup{
96 GroupField: proto.Int32(8),
97 },
98
99
100 XXX_unrecognized: []byte{13<<3 | 0, 4},
101 }
102 ext := &pb.Ext{
103 Data: proto.String("Big gobs for big rats"),
104 }
105 if err := proto.SetExtension(msg, pb.E_Ext_More, ext); err != nil {
106 panic(err)
107 }
108 greetings := []string{"adg", "easy", "cow"}
109 if err := proto.SetExtension(msg, pb.E_Greeting, greetings); err != nil {
110 panic(err)
111 }
112
113
114 b, err := proto.Marshal(&pb.Ext{Data: proto.String("3G skiing")})
115 if err != nil {
116 panic(err)
117 }
118 b = append(proto.EncodeVarint(201<<3|proto.WireBytes), b...)
119 proto.SetRawExtension(msg, 201, b)
120
121
122 b = append(proto.EncodeVarint(202<<3|proto.WireVarint), 19)
123 proto.SetRawExtension(msg, 202, b)
124
125 return msg
126 }
127
128 const text = `count: 42
129 name: "Dave"
130 quote: "\"I didn't want to go.\""
131 pet: "bunny"
132 pet: "kitty"
133 pet: "horsey"
134 inner: <
135 host: "footrest.syd"
136 port: 7001
137 connected: true
138 >
139 others: <
140 key: 3735928559
141 value: "\001A\007\014"
142 >
143 others: <
144 weight: 6.022
145 inner: <
146 host: "lesha.mtv"
147 port: 8002
148 >
149 >
150 bikeshed: BLUE
151 SomeGroup {
152 group_field: 8
153 }
154 /* 2 unknown bytes */
155 13: 4
156 [test_proto.Ext.more]: <
157 data: "Big gobs for big rats"
158 >
159 [test_proto.greeting]: "adg"
160 [test_proto.greeting]: "easy"
161 [test_proto.greeting]: "cow"
162 /* 13 unknown bytes */
163 201: "\t3G skiing"
164 /* 3 unknown bytes */
165 202: 19
166 `
167
168 func TestMarshalText(t *testing.T) {
169 buf := new(bytes.Buffer)
170 if err := proto.MarshalText(buf, newTestMessage()); err != nil {
171 t.Fatalf("proto.MarshalText: %v", err)
172 }
173 s := buf.String()
174 if s != text {
175 t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v===\n", s, text)
176 }
177 }
178
179 func TestMarshalTextCustomMessage(t *testing.T) {
180 buf := new(bytes.Buffer)
181 if err := proto.MarshalText(buf, &textMessage{}); err != nil {
182 t.Fatalf("proto.MarshalText: %v", err)
183 }
184 s := buf.String()
185 if s != "custom" {
186 t.Errorf("Got %q, expected %q", s, "custom")
187 }
188 }
189 func TestMarshalTextNil(t *testing.T) {
190 want := "<nil>"
191 tests := []proto.Message{nil, (*pb.MyMessage)(nil)}
192 for i, test := range tests {
193 buf := new(bytes.Buffer)
194 if err := proto.MarshalText(buf, test); err != nil {
195 t.Fatal(err)
196 }
197 if got := buf.String(); got != want {
198 t.Errorf("%d: got %q want %q", i, got, want)
199 }
200 }
201 }
202
203 func TestMarshalTextUnknownEnum(t *testing.T) {
204
205 m := &pb.MyMessage{Bikeshed: pb.MyMessage_Color(3).Enum()}
206 got := m.String()
207 const want = `bikeshed:3 `
208 if got != want {
209 t.Errorf("\n got %q\nwant %q", got, want)
210 }
211 }
212
213 func TestTextOneof(t *testing.T) {
214 tests := []struct {
215 m proto.Message
216 want string
217 }{
218
219 {&pb.Communique{}, ``},
220
221 {&pb.Communique{Union: &pb.Communique_Number{Number: 4}}, `number:4`},
222
223 {&pb.Communique{Union: &pb.Communique_Msg{
224 Msg: &pb.Strings{StringField: proto.String("why hello!")},
225 }}, `msg:<string_field:"why hello!" >`},
226
227 {&pb.Communique{Union: &pb.Communique_Msg{Msg: nil}}, `msg:/* nil */`},
228 }
229 for _, test := range tests {
230 got := strings.TrimSpace(test.m.String())
231 if got != test.want {
232 t.Errorf("\n got %s\nwant %s", got, test.want)
233 }
234 }
235 }
236
237 func BenchmarkMarshalTextBuffered(b *testing.B) {
238 buf := new(bytes.Buffer)
239 m := newTestMessage()
240 for i := 0; i < b.N; i++ {
241 buf.Reset()
242 proto.MarshalText(buf, m)
243 }
244 }
245
246 func BenchmarkMarshalTextUnbuffered(b *testing.B) {
247 w := ioutil.Discard
248 m := newTestMessage()
249 for i := 0; i < b.N; i++ {
250 proto.MarshalText(w, m)
251 }
252 }
253
254 func compact(src string) string {
255
256 dst := make([]byte, len(src))
257 space, comment := false, false
258 j := 0
259 for i := 0; i < len(src); i++ {
260 if strings.HasPrefix(src[i:], "/*") {
261 comment = true
262 i++
263 continue
264 }
265 if comment && strings.HasPrefix(src[i:], "*/") {
266 comment = false
267 i++
268 continue
269 }
270 if comment {
271 continue
272 }
273 c := src[i]
274 if c == ' ' || c == '\n' {
275 space = true
276 continue
277 }
278 if j > 0 && (dst[j-1] == ':' || dst[j-1] == '<' || dst[j-1] == '{') {
279 space = false
280 }
281 if c == '{' {
282 space = false
283 }
284 if space {
285 dst[j] = ' '
286 j++
287 space = false
288 }
289 dst[j] = c
290 j++
291 }
292 if space {
293 dst[j] = ' '
294 j++
295 }
296 return string(dst[0:j])
297 }
298
299 var compactText = compact(text)
300
301 func TestCompactText(t *testing.T) {
302 s := proto.CompactTextString(newTestMessage())
303 if s != compactText {
304 t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v\n===\n", s, compactText)
305 }
306 }
307
308 func TestStringEscaping(t *testing.T) {
309 testCases := []struct {
310 in *pb.Strings
311 out string
312 }{
313 {
314
315
316 &pb.Strings{StringField: proto.String("\"A string with ' characters \n and \r newlines and \t tabs and \001 slashes \\ and multiple spaces")},
317 "string_field: \"\\\"A string with ' characters \\n and \\r newlines and \\t tabs and \\001 slashes \\\\ and multiple spaces\"\n",
318 },
319 {
320
321 &pb.Strings{StringField: proto.String("\350\260\267\346\255\214")},
322 "string_field: \"\\350\\260\\267\\346\\255\\214\"\n",
323 },
324 {
325
326 &pb.Strings{StringField: proto.String("\x00\x01\xff\x81")},
327 `string_field: "\000\001\377\201"` + "\n",
328 },
329 }
330
331 for i, tc := range testCases {
332 var buf bytes.Buffer
333 if err := proto.MarshalText(&buf, tc.in); err != nil {
334 t.Errorf("proto.MarsalText: %v", err)
335 continue
336 }
337 s := buf.String()
338 if s != tc.out {
339 t.Errorf("#%d: Got:\n%s\nExpected:\n%s\n", i, s, tc.out)
340 continue
341 }
342
343
344 pbStrings := new(pb.Strings)
345 if err := proto.UnmarshalText(s, pbStrings); err != nil {
346 t.Errorf("#%d: UnmarshalText: %v", i, err)
347 continue
348 }
349 if !proto.Equal(pbStrings, tc.in) {
350 t.Errorf("#%d: Round-trip failed:\nstart: %v\n end: %v", i, tc.in, pbStrings)
351 }
352 }
353 }
354
355
356
357
358 type limitedWriter struct {
359 b bytes.Buffer
360 limit int
361 }
362
363 var outOfSpace = errors.New("proto: insufficient space")
364
365 func (w *limitedWriter) Write(p []byte) (n int, err error) {
366 var avail = w.limit - w.b.Len()
367 if avail <= 0 {
368 return 0, outOfSpace
369 }
370 if len(p) <= avail {
371 return w.b.Write(p)
372 }
373 n, _ = w.b.Write(p[:avail])
374 return n, outOfSpace
375 }
376
377 func TestMarshalTextFailing(t *testing.T) {
378
379 for lim := 0; lim < len(text); lim++ {
380 buf := new(limitedWriter)
381 buf.limit = lim
382 err := proto.MarshalText(buf, newTestMessage())
383
384 if err != outOfSpace {
385 t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v===\n", err, outOfSpace)
386 }
387 s := buf.b.String()
388 x := text[:buf.limit]
389 if s != x {
390 t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v===\n", s, x)
391 }
392 }
393 }
394
395 func TestFloats(t *testing.T) {
396 tests := []struct {
397 f float64
398 want string
399 }{
400 {0, "0"},
401 {4.7, "4.7"},
402 {math.Inf(1), "inf"},
403 {math.Inf(-1), "-inf"},
404 {math.NaN(), "nan"},
405 }
406 for _, test := range tests {
407 msg := &pb.FloatingPoint{F: &test.f}
408 got := strings.TrimSpace(msg.String())
409 want := `f:` + test.want
410 if got != want {
411 t.Errorf("f=%f: got %q, want %q", test.f, got, want)
412 }
413 }
414 }
415
416 func TestRepeatedNilText(t *testing.T) {
417 m := &pb.MessageList{
418 Message: []*pb.MessageList_Message{
419 nil,
420 {
421 Name: proto.String("Horse"),
422 },
423 nil,
424 },
425 }
426 want := `Message <nil>
427 Message {
428 name: "Horse"
429 }
430 Message <nil>
431 `
432 if s := proto.MarshalTextString(m); s != want {
433 t.Errorf(" got: %s\nwant: %s", s, want)
434 }
435 }
436
437 func TestProto3Text(t *testing.T) {
438 tests := []struct {
439 m proto.Message
440 want string
441 }{
442
443 {&proto3pb.Message{}, ``},
444
445 {&proto3pb.Message{Data: []byte{}}, ``},
446
447 {&proto3pb.Message{Name: "Rob", HeightInCm: 175}, `name:"Rob" height_in_cm:175`},
448
449 {&pb.MessageWithMap{}, ``},
450
451
452 {
453 &pb.MessageWithMap{NameMapping: map[int32]string{
454 -1: "Negatory",
455 7: "Lucky",
456 1234: "Feist",
457 6345789: "Otis",
458 }},
459 `name_mapping:<key:-1 value:"Negatory" > ` +
460 `name_mapping:<key:7 value:"Lucky" > ` +
461 `name_mapping:<key:1234 value:"Feist" > ` +
462 `name_mapping:<key:6345789 value:"Otis" >`,
463 },
464
465 {
466 &pb.MessageWithMap{MsgMapping: map[int64]*pb.FloatingPoint{7: nil}},
467 `msg_mapping:<key:7 >`,
468 },
469 }
470 for _, test := range tests {
471 got := strings.TrimSpace(test.m.String())
472 if got != test.want {
473 t.Errorf("\n got %s\nwant %s", got, test.want)
474 }
475 }
476 }
477
478 func TestRacyMarshal(t *testing.T) {
479
480
481 any := &pb.MyMessage{Count: proto.Int32(47), Name: proto.String("David")}
482 proto.SetExtension(any, pb.E_Ext_Text, proto.String("bar"))
483 b, err := proto.Marshal(any)
484 if err != nil {
485 panic(err)
486 }
487 m := &proto3pb.Message{
488 Name: "David",
489 ResultCount: 47,
490 Anything: &types.Any{TypeUrl: "type.googleapis.com/" + proto.MessageName(any), Value: b},
491 }
492
493 wantText := proto.MarshalTextString(m)
494 wantBytes, err := proto.Marshal(m)
495 if err != nil {
496 t.Fatalf("proto.Marshal error: %v", err)
497 }
498
499 var wg sync.WaitGroup
500 defer wg.Wait()
501 wg.Add(20)
502 for i := 0; i < 10; i++ {
503 go func() {
504 defer wg.Done()
505 got := proto.MarshalTextString(m)
506 if got != wantText {
507 t.Errorf("proto.MarshalTextString = %q, want %q", got, wantText)
508 }
509 }()
510 go func() {
511 defer wg.Done()
512 got, err := proto.Marshal(m)
513 if !bytes.Equal(got, wantBytes) || err != nil {
514 t.Errorf("proto.Marshal = (%x, %v), want (%x, nil)", got, err, wantBytes)
515 }
516 }()
517 }
518 }
519
View as plain text