OSDN Git Service

Merge pull request #41 from Bytom/dev
[bytom/vapor.git] / vendor / github.com / gogo / protobuf / proto / text_test.go
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
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 // textMessage implements the methods that allow it to marshal and unmarshal
51 // itself as text.
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                 // One normally wouldn't do this.
99                 // This is an undeclared tag 13, as a varint (wire type 0) with value 4.
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         // Add an unknown extension. We marshal a pb.Ext, and fake the ID.
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         // Extensions can be plain fields, too, so let's test that.
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         // The Color enum only specifies values 0-2.
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                 // zero message
219                 {&pb.Communique{}, ``},
220                 // scalar field
221                 {&pb.Communique{Union: &pb.Communique_Number{Number: 4}}, `number:4`},
222                 // message field
223                 {&pb.Communique{Union: &pb.Communique_Msg{
224                         Msg: &pb.Strings{StringField: proto.String("why hello!")},
225                 }}, `msg:<string_field:"why hello!" >`},
226                 // bad oneof (should not panic)
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         // s/[ \n]+/ /g; s/ $//;
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                         // Test data from C++ test (TextFormatTest.StringEscape).
315                         // Single divergence: we don't escape apostrophes.
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                         // Test data from the same C++ test.
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                         // Some UTF-8.
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                 // Check round-trip.
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 // A limitedWriter accepts some output before it fails.
356 // This is a proxy for something like a nearly-full or imminently-failing disk,
357 // or a network connection that is about to die.
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         // Try lots of different sizes to exercise more error code-paths.
379         for lim := 0; lim < len(text); lim++ {
380                 buf := new(limitedWriter)
381                 buf.limit = lim
382                 err := proto.MarshalText(buf, newTestMessage())
383                 // We expect a certain error, but also some partial results in the buffer.
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                 // zero message
443                 {&proto3pb.Message{}, ``},
444                 // zero message except for an empty byte slice
445                 {&proto3pb.Message{Data: []byte{}}, ``},
446                 // trivial case
447                 {&proto3pb.Message{Name: "Rob", HeightInCm: 175}, `name:"Rob" height_in_cm:175`},
448                 // empty map
449                 {&pb.MessageWithMap{}, ``},
450                 // non-empty map; map format is the same as a repeated struct,
451                 // and they are sorted by key (numerically for numeric keys).
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                 // map with nil value; not well-defined, but we shouldn't crash
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         // This test should be run with the race detector.
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 }