1 // Go support for Protocol Buffers - Google's data interchange format
3 // Copyright 2010 The Go Authors. All rights reserved.
4 // https://github.com/golang/protobuf
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions are
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
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.
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.
42 "github.com/golang/protobuf/proto"
44 proto3pb "github.com/golang/protobuf/proto/proto3_proto"
45 pb "github.com/golang/protobuf/proto/testdata"
48 // textMessage implements the methods that allow it to marshal and unmarshal
50 type textMessage struct {
53 func (*textMessage) MarshalText() ([]byte, error) {
54 return []byte("custom"), nil
57 func (*textMessage) UnmarshalText(bytes []byte) error {
58 if string(bytes) != "custom" {
59 return errors.New("expected 'custom'")
64 func (*textMessage) Reset() {}
65 func (*textMessage) String() string { return "" }
66 func (*textMessage) ProtoMessage() {}
68 func newTestMessage() *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),
79 Others: []*pb.OtherMessage{
81 Key: proto.Int64(0xdeadbeef),
82 Value: []byte{1, 65, 7, 12},
85 Weight: proto.Float32(6.022),
86 Inner: &pb.InnerMessage{
87 Host: proto.String("lesha.mtv"),
88 Port: proto.Int32(8002),
92 Bikeshed: pb.MyMessage_BLUE.Enum(),
93 Somegroup: &pb.MyMessage_SomeGroup{
94 GroupField: proto.Int32(8),
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},
101 Data: proto.String("Big gobs for big rats"),
103 if err := proto.SetExtension(msg, pb.E_Ext_More, ext); err != nil {
106 greetings := []string{"adg", "easy", "cow"}
107 if err := proto.SetExtension(msg, pb.E_Greeting, greetings); err != nil {
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")})
116 b = append(proto.EncodeVarint(201<<3|proto.WireBytes), b...)
117 proto.SetRawExtension(msg, 201, b)
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)
126 const text = `count: 42
128 quote: "\"I didn't want to go.\""
139 value: "\001A\007\014"
152 /* 2 unknown bytes */
154 [testdata.Ext.more]: <
155 data: "Big gobs for big rats"
157 [testdata.greeting]: "adg"
158 [testdata.greeting]: "easy"
159 [testdata.greeting]: "cow"
160 /* 13 unknown bytes */
162 /* 3 unknown bytes */
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)
173 t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v===\n", s, text)
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)
184 t.Errorf("Got %q, expected %q", s, "custom")
187 func TestMarshalTextNil(t *testing.T) {
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 {
195 if got := buf.String(); got != want {
196 t.Errorf("%d: got %q want %q", i, got, want)
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()}
205 const want = `bikeshed:3 `
207 t.Errorf("\n got %q\nwant %q", got, want)
211 func TestTextOneof(t *testing.T) {
217 {&pb.Communique{}, ``},
219 {&pb.Communique{Union: &pb.Communique_Number{4}}, `number:4`},
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 */`},
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)
235 func BenchmarkMarshalTextBuffered(b *testing.B) {
236 buf := new(bytes.Buffer)
237 m := newTestMessage()
238 for i := 0; i < b.N; i++ {
240 proto.MarshalText(buf, m)
244 func BenchmarkMarshalTextUnbuffered(b *testing.B) {
246 m := newTestMessage()
247 for i := 0; i < b.N; i++ {
248 proto.MarshalText(w, m)
252 func compact(src string) string {
253 // s/[ \n]+/ /g; s/ $//;
254 dst := make([]byte, len(src))
255 space, comment := false, false
257 for i := 0; i < len(src); i++ {
258 if strings.HasPrefix(src[i:], "/*") {
263 if comment && strings.HasPrefix(src[i:], "*/") {
272 if c == ' ' || c == '\n' {
276 if j > 0 && (dst[j-1] == ':' || dst[j-1] == '<' || dst[j-1] == '{') {
294 return string(dst[0:j])
297 var compactText = compact(text)
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)
306 func TestStringEscaping(t *testing.T) {
307 testCases := []struct {
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",
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",
324 &pb.Strings{StringField: proto.String("\x00\x01\xff\x81")},
325 `string_field: "\000\001\377\201"` + "\n",
329 for i, tc := range testCases {
331 if err := proto.MarshalText(&buf, tc.in); err != nil {
332 t.Errorf("proto.MarsalText: %v", err)
337 t.Errorf("#%d: Got:\n%s\nExpected:\n%s\n", i, s, tc.out)
342 pb := new(pb.Strings)
343 if err := proto.UnmarshalText(s, pb); err != nil {
344 t.Errorf("#%d: UnmarshalText: %v", i, err)
347 if !proto.Equal(pb, tc.in) {
348 t.Errorf("#%d: Round-trip failed:\nstart: %v\n end: %v", i, tc.in, pb)
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 {
361 var outOfSpace = errors.New("proto: insufficient space")
363 func (w *limitedWriter) Write(p []byte) (n int, err error) {
364 var avail = w.limit - w.b.Len()
371 n, _ = w.b.Write(p[:avail])
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)
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)
386 x := text[:buf.limit]
388 t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v===\n", s, x)
393 func TestFloats(t *testing.T) {
400 {math.Inf(1), "inf"},
401 {math.Inf(-1), "-inf"},
404 for _, test := range tests {
405 msg := &pb.FloatingPoint{F: &test.f}
406 got := strings.TrimSpace(msg.String())
407 want := `f:` + test.want
409 t.Errorf("f=%f: got %q, want %q", test.f, got, want)
414 func TestRepeatedNilText(t *testing.T) {
415 m := &pb.MessageList{
416 Message: []*pb.MessageList_Message{
418 &pb.MessageList_Message{
419 Name: proto.String("Horse"),
424 want := `Message <nil>
430 if s := proto.MarshalTextString(m); s != want {
431 t.Errorf(" got: %s\nwant: %s", s, want)
435 func TestProto3Text(t *testing.T) {
441 {&proto3pb.Message{}, ``},
442 // zero message except for an empty byte slice
443 {&proto3pb.Message{Data: []byte{}}, ``},
445 {&proto3pb.Message{Name: "Rob", HeightInCm: 175}, `name:"Rob" height_in_cm:175`},
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).
451 &pb.MessageWithMap{NameMapping: map[int32]string{
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" >`,
462 // map with nil value; not well-defined, but we shouldn't crash
464 &pb.MessageWithMap{MsgMapping: map[int64]*pb.FloatingPoint{7: nil}},
465 `msg_mapping:<key:7 >`,
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)