OSDN Git Service

Merge pull request #41 from Bytom/dev
[bytom/vapor.git] / vendor / github.com / golang / 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         "testing"
41
42         "github.com/golang/protobuf/proto"
43
44         proto3pb "github.com/golang/protobuf/proto/proto3_proto"
45         pb "github.com/golang/protobuf/proto/testdata"
46 )
47
48 // textMessage implements the methods that allow it to marshal and unmarshal
49 // itself as text.
50 type textMessage struct {
51 }
52
53 func (*textMessage) MarshalText() ([]byte, error) {
54         return []byte("custom"), nil
55 }
56
57 func (*textMessage) UnmarshalText(bytes []byte) error {
58         if string(bytes) != "custom" {
59                 return errors.New("expected 'custom'")
60         }
61         return nil
62 }
63
64 func (*textMessage) Reset()         {}
65 func (*textMessage) String() string { return "" }
66 func (*textMessage) ProtoMessage()  {}
67
68 func newTestMessage() *pb.MyMessage {
69         msg := &pb.MyMessage{
70                 Count: proto.Int32(42),
71                 Name:  proto.String("Dave"),
72                 Quote: proto.String(`"I didn't want to go."`),
73                 Pet:   []string{"bunny", "kitty", "horsey"},
74                 Inner: &pb.InnerMessage{
75                         Host:      proto.String("footrest.syd"),
76                         Port:      proto.Int32(7001),
77                         Connected: proto.Bool(true),
78                 },
79                 Others: []*pb.OtherMessage{
80                         {
81                                 Key:   proto.Int64(0xdeadbeef),
82                                 Value: []byte{1, 65, 7, 12},
83                         },
84                         {
85                                 Weight: proto.Float32(6.022),
86                                 Inner: &pb.InnerMessage{
87                                         Host: proto.String("lesha.mtv"),
88                                         Port: proto.Int32(8002),
89                                 },
90                         },
91                 },
92                 Bikeshed: pb.MyMessage_BLUE.Enum(),
93                 Somegroup: &pb.MyMessage_SomeGroup{
94                         GroupField: proto.Int32(8),
95                 },
96                 // One normally wouldn't do this.
97                 // This is an undeclared tag 13, as a varint (wire type 0) with value 4.
98                 XXX_unrecognized: []byte{13<<3 | 0, 4},
99         }
100         ext := &pb.Ext{
101                 Data: proto.String("Big gobs for big rats"),
102         }
103         if err := proto.SetExtension(msg, pb.E_Ext_More, ext); err != nil {
104                 panic(err)
105         }
106         greetings := []string{"adg", "easy", "cow"}
107         if err := proto.SetExtension(msg, pb.E_Greeting, greetings); err != nil {
108                 panic(err)
109         }
110
111         // Add an unknown extension. We marshal a pb.Ext, and fake the ID.
112         b, err := proto.Marshal(&pb.Ext{Data: proto.String("3G skiing")})
113         if err != nil {
114                 panic(err)
115         }
116         b = append(proto.EncodeVarint(201<<3|proto.WireBytes), b...)
117         proto.SetRawExtension(msg, 201, b)
118
119         // Extensions can be plain fields, too, so let's test that.
120         b = append(proto.EncodeVarint(202<<3|proto.WireVarint), 19)
121         proto.SetRawExtension(msg, 202, b)
122
123         return msg
124 }
125
126 const text = `count: 42
127 name: "Dave"
128 quote: "\"I didn't want to go.\""
129 pet: "bunny"
130 pet: "kitty"
131 pet: "horsey"
132 inner: <
133   host: "footrest.syd"
134   port: 7001
135   connected: true
136 >
137 others: <
138   key: 3735928559
139   value: "\001A\007\014"
140 >
141 others: <
142   weight: 6.022
143   inner: <
144     host: "lesha.mtv"
145     port: 8002
146   >
147 >
148 bikeshed: BLUE
149 SomeGroup {
150   group_field: 8
151 }
152 /* 2 unknown bytes */
153 13: 4
154 [testdata.Ext.more]: <
155   data: "Big gobs for big rats"
156 >
157 [testdata.greeting]: "adg"
158 [testdata.greeting]: "easy"
159 [testdata.greeting]: "cow"
160 /* 13 unknown bytes */
161 201: "\t3G skiing"
162 /* 3 unknown bytes */
163 202: 19
164 `
165
166 func TestMarshalText(t *testing.T) {
167         buf := new(bytes.Buffer)
168         if err := proto.MarshalText(buf, newTestMessage()); err != nil {
169                 t.Fatalf("proto.MarshalText: %v", err)
170         }
171         s := buf.String()
172         if s != text {
173                 t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v===\n", s, text)
174         }
175 }
176
177 func TestMarshalTextCustomMessage(t *testing.T) {
178         buf := new(bytes.Buffer)
179         if err := proto.MarshalText(buf, &textMessage{}); err != nil {
180                 t.Fatalf("proto.MarshalText: %v", err)
181         }
182         s := buf.String()
183         if s != "custom" {
184                 t.Errorf("Got %q, expected %q", s, "custom")
185         }
186 }
187 func TestMarshalTextNil(t *testing.T) {
188         want := "<nil>"
189         tests := []proto.Message{nil, (*pb.MyMessage)(nil)}
190         for i, test := range tests {
191                 buf := new(bytes.Buffer)
192                 if err := proto.MarshalText(buf, test); err != nil {
193                         t.Fatal(err)
194                 }
195                 if got := buf.String(); got != want {
196                         t.Errorf("%d: got %q want %q", i, got, want)
197                 }
198         }
199 }
200
201 func TestMarshalTextUnknownEnum(t *testing.T) {
202         // The Color enum only specifies values 0-2.
203         m := &pb.MyMessage{Bikeshed: pb.MyMessage_Color(3).Enum()}
204         got := m.String()
205         const want = `bikeshed:3 `
206         if got != want {
207                 t.Errorf("\n got %q\nwant %q", got, want)
208         }
209 }
210
211 func TestTextOneof(t *testing.T) {
212         tests := []struct {
213                 m    proto.Message
214                 want string
215         }{
216                 // zero message
217                 {&pb.Communique{}, ``},
218                 // scalar field
219                 {&pb.Communique{Union: &pb.Communique_Number{4}}, `number:4`},
220                 // message field
221                 {&pb.Communique{Union: &pb.Communique_Msg{
222                         &pb.Strings{StringField: proto.String("why hello!")},
223                 }}, `msg:<string_field:"why hello!" >`},
224                 // bad oneof (should not panic)
225                 {&pb.Communique{Union: &pb.Communique_Msg{nil}}, `msg:/* nil */`},
226         }
227         for _, test := range tests {
228                 got := strings.TrimSpace(test.m.String())
229                 if got != test.want {
230                         t.Errorf("\n got %s\nwant %s", got, test.want)
231                 }
232         }
233 }
234
235 func BenchmarkMarshalTextBuffered(b *testing.B) {
236         buf := new(bytes.Buffer)
237         m := newTestMessage()
238         for i := 0; i < b.N; i++ {
239                 buf.Reset()
240                 proto.MarshalText(buf, m)
241         }
242 }
243
244 func BenchmarkMarshalTextUnbuffered(b *testing.B) {
245         w := ioutil.Discard
246         m := newTestMessage()
247         for i := 0; i < b.N; i++ {
248                 proto.MarshalText(w, m)
249         }
250 }
251
252 func compact(src string) string {
253         // s/[ \n]+/ /g; s/ $//;
254         dst := make([]byte, len(src))
255         space, comment := false, false
256         j := 0
257         for i := 0; i < len(src); i++ {
258                 if strings.HasPrefix(src[i:], "/*") {
259                         comment = true
260                         i++
261                         continue
262                 }
263                 if comment && strings.HasPrefix(src[i:], "*/") {
264                         comment = false
265                         i++
266                         continue
267                 }
268                 if comment {
269                         continue
270                 }
271                 c := src[i]
272                 if c == ' ' || c == '\n' {
273                         space = true
274                         continue
275                 }
276                 if j > 0 && (dst[j-1] == ':' || dst[j-1] == '<' || dst[j-1] == '{') {
277                         space = false
278                 }
279                 if c == '{' {
280                         space = false
281                 }
282                 if space {
283                         dst[j] = ' '
284                         j++
285                         space = false
286                 }
287                 dst[j] = c
288                 j++
289         }
290         if space {
291                 dst[j] = ' '
292                 j++
293         }
294         return string(dst[0:j])
295 }
296
297 var compactText = compact(text)
298
299 func TestCompactText(t *testing.T) {
300         s := proto.CompactTextString(newTestMessage())
301         if s != compactText {
302                 t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v\n===\n", s, compactText)
303         }
304 }
305
306 func TestStringEscaping(t *testing.T) {
307         testCases := []struct {
308                 in  *pb.Strings
309                 out string
310         }{
311                 {
312                         // Test data from C++ test (TextFormatTest.StringEscape).
313                         // Single divergence: we don't escape apostrophes.
314                         &pb.Strings{StringField: proto.String("\"A string with ' characters \n and \r newlines and \t tabs and \001 slashes \\ and  multiple   spaces")},
315                         "string_field: \"\\\"A string with ' characters \\n and \\r newlines and \\t tabs and \\001 slashes \\\\ and  multiple   spaces\"\n",
316                 },
317                 {
318                         // Test data from the same C++ test.
319                         &pb.Strings{StringField: proto.String("\350\260\267\346\255\214")},
320                         "string_field: \"\\350\\260\\267\\346\\255\\214\"\n",
321                 },
322                 {
323                         // Some UTF-8.
324                         &pb.Strings{StringField: proto.String("\x00\x01\xff\x81")},
325                         `string_field: "\000\001\377\201"` + "\n",
326                 },
327         }
328
329         for i, tc := range testCases {
330                 var buf bytes.Buffer
331                 if err := proto.MarshalText(&buf, tc.in); err != nil {
332                         t.Errorf("proto.MarsalText: %v", err)
333                         continue
334                 }
335                 s := buf.String()
336                 if s != tc.out {
337                         t.Errorf("#%d: Got:\n%s\nExpected:\n%s\n", i, s, tc.out)
338                         continue
339                 }
340
341                 // Check round-trip.
342                 pb := new(pb.Strings)
343                 if err := proto.UnmarshalText(s, pb); err != nil {
344                         t.Errorf("#%d: UnmarshalText: %v", i, err)
345                         continue
346                 }
347                 if !proto.Equal(pb, tc.in) {
348                         t.Errorf("#%d: Round-trip failed:\nstart: %v\n  end: %v", i, tc.in, pb)
349                 }
350         }
351 }
352
353 // A limitedWriter accepts some output before it fails.
354 // This is a proxy for something like a nearly-full or imminently-failing disk,
355 // or a network connection that is about to die.
356 type limitedWriter struct {
357         b     bytes.Buffer
358         limit int
359 }
360
361 var outOfSpace = errors.New("proto: insufficient space")
362
363 func (w *limitedWriter) Write(p []byte) (n int, err error) {
364         var avail = w.limit - w.b.Len()
365         if avail <= 0 {
366                 return 0, outOfSpace
367         }
368         if len(p) <= avail {
369                 return w.b.Write(p)
370         }
371         n, _ = w.b.Write(p[:avail])
372         return n, outOfSpace
373 }
374
375 func TestMarshalTextFailing(t *testing.T) {
376         // Try lots of different sizes to exercise more error code-paths.
377         for lim := 0; lim < len(text); lim++ {
378                 buf := new(limitedWriter)
379                 buf.limit = lim
380                 err := proto.MarshalText(buf, newTestMessage())
381                 // We expect a certain error, but also some partial results in the buffer.
382                 if err != outOfSpace {
383                         t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v===\n", err, outOfSpace)
384                 }
385                 s := buf.b.String()
386                 x := text[:buf.limit]
387                 if s != x {
388                         t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v===\n", s, x)
389                 }
390         }
391 }
392
393 func TestFloats(t *testing.T) {
394         tests := []struct {
395                 f    float64
396                 want string
397         }{
398                 {0, "0"},
399                 {4.7, "4.7"},
400                 {math.Inf(1), "inf"},
401                 {math.Inf(-1), "-inf"},
402                 {math.NaN(), "nan"},
403         }
404         for _, test := range tests {
405                 msg := &pb.FloatingPoint{F: &test.f}
406                 got := strings.TrimSpace(msg.String())
407                 want := `f:` + test.want
408                 if got != want {
409                         t.Errorf("f=%f: got %q, want %q", test.f, got, want)
410                 }
411         }
412 }
413
414 func TestRepeatedNilText(t *testing.T) {
415         m := &pb.MessageList{
416                 Message: []*pb.MessageList_Message{
417                         nil,
418                         &pb.MessageList_Message{
419                                 Name: proto.String("Horse"),
420                         },
421                         nil,
422                 },
423         }
424         want := `Message <nil>
425 Message {
426   name: "Horse"
427 }
428 Message <nil>
429 `
430         if s := proto.MarshalTextString(m); s != want {
431                 t.Errorf(" got: %s\nwant: %s", s, want)
432         }
433 }
434
435 func TestProto3Text(t *testing.T) {
436         tests := []struct {
437                 m    proto.Message
438                 want string
439         }{
440                 // zero message
441                 {&proto3pb.Message{}, ``},
442                 // zero message except for an empty byte slice
443                 {&proto3pb.Message{Data: []byte{}}, ``},
444                 // trivial case
445                 {&proto3pb.Message{Name: "Rob", HeightInCm: 175}, `name:"Rob" height_in_cm:175`},
446                 // empty map
447                 {&pb.MessageWithMap{}, ``},
448                 // non-empty map; map format is the same as a repeated struct,
449                 // and they are sorted by key (numerically for numeric keys).
450                 {
451                         &pb.MessageWithMap{NameMapping: map[int32]string{
452                                 -1:      "Negatory",
453                                 7:       "Lucky",
454                                 1234:    "Feist",
455                                 6345789: "Otis",
456                         }},
457                         `name_mapping:<key:-1 value:"Negatory" > ` +
458                                 `name_mapping:<key:7 value:"Lucky" > ` +
459                                 `name_mapping:<key:1234 value:"Feist" > ` +
460                                 `name_mapping:<key:6345789 value:"Otis" >`,
461                 },
462                 // map with nil value; not well-defined, but we shouldn't crash
463                 {
464                         &pb.MessageWithMap{MsgMapping: map[int64]*pb.FloatingPoint{7: nil}},
465                         `msg_mapping:<key:7 >`,
466                 },
467         }
468         for _, test := range tests {
469                 got := strings.TrimSpace(test.m.String())
470                 if got != test.want {
471                         t.Errorf("\n got %s\nwant %s", got, test.want)
472                 }
473         }
474 }