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.
43 "github.com/gogo/protobuf/proto"
45 proto3pb "github.com/gogo/protobuf/proto/proto3_proto"
46 pb "github.com/gogo/protobuf/proto/test_proto"
47 "github.com/gogo/protobuf/types"
50 // textMessage implements the methods that allow it to marshal and unmarshal
52 type textMessage struct {
55 func (*textMessage) MarshalText() ([]byte, error) {
56 return []byte("custom"), nil
59 func (*textMessage) UnmarshalText(bytes []byte) error {
60 if string(bytes) != "custom" {
61 return errors.New("expected 'custom'")
66 func (*textMessage) Reset() {}
67 func (*textMessage) String() string { return "" }
68 func (*textMessage) ProtoMessage() {}
70 func newTestMessage() *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),
81 Others: []*pb.OtherMessage{
83 Key: proto.Int64(0xdeadbeef),
84 Value: []byte{1, 65, 7, 12},
87 Weight: proto.Float32(6.022),
88 Inner: &pb.InnerMessage{
89 Host: proto.String("lesha.mtv"),
90 Port: proto.Int32(8002),
94 Bikeshed: pb.MyMessage_BLUE.Enum(),
95 Somegroup: &pb.MyMessage_SomeGroup{
96 GroupField: proto.Int32(8),
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},
103 Data: proto.String("Big gobs for big rats"),
105 if err := proto.SetExtension(msg, pb.E_Ext_More, ext); err != nil {
108 greetings := []string{"adg", "easy", "cow"}
109 if err := proto.SetExtension(msg, pb.E_Greeting, greetings); err != nil {
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")})
118 b = append(proto.EncodeVarint(201<<3|proto.WireBytes), b...)
119 proto.SetRawExtension(msg, 201, b)
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)
128 const text = `count: 42
130 quote: "\"I didn't want to go.\""
141 value: "\001A\007\014"
154 /* 2 unknown bytes */
156 [test_proto.Ext.more]: <
157 data: "Big gobs for big rats"
159 [test_proto.greeting]: "adg"
160 [test_proto.greeting]: "easy"
161 [test_proto.greeting]: "cow"
162 /* 13 unknown bytes */
164 /* 3 unknown bytes */
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)
175 t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v===\n", s, text)
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)
186 t.Errorf("Got %q, expected %q", s, "custom")
189 func TestMarshalTextNil(t *testing.T) {
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 {
197 if got := buf.String(); got != want {
198 t.Errorf("%d: got %q want %q", i, got, want)
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()}
207 const want = `bikeshed:3 `
209 t.Errorf("\n got %q\nwant %q", got, want)
213 func TestTextOneof(t *testing.T) {
219 {&pb.Communique{}, ``},
221 {&pb.Communique{Union: &pb.Communique_Number{Number: 4}}, `number:4`},
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 */`},
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)
237 func BenchmarkMarshalTextBuffered(b *testing.B) {
238 buf := new(bytes.Buffer)
239 m := newTestMessage()
240 for i := 0; i < b.N; i++ {
242 proto.MarshalText(buf, m)
246 func BenchmarkMarshalTextUnbuffered(b *testing.B) {
248 m := newTestMessage()
249 for i := 0; i < b.N; i++ {
250 proto.MarshalText(w, m)
254 func compact(src string) string {
255 // s/[ \n]+/ /g; s/ $//;
256 dst := make([]byte, len(src))
257 space, comment := false, false
259 for i := 0; i < len(src); i++ {
260 if strings.HasPrefix(src[i:], "/*") {
265 if comment && strings.HasPrefix(src[i:], "*/") {
274 if c == ' ' || c == '\n' {
278 if j > 0 && (dst[j-1] == ':' || dst[j-1] == '<' || dst[j-1] == '{') {
296 return string(dst[0:j])
299 var compactText = compact(text)
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)
308 func TestStringEscaping(t *testing.T) {
309 testCases := []struct {
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",
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",
326 &pb.Strings{StringField: proto.String("\x00\x01\xff\x81")},
327 `string_field: "\000\001\377\201"` + "\n",
331 for i, tc := range testCases {
333 if err := proto.MarshalText(&buf, tc.in); err != nil {
334 t.Errorf("proto.MarsalText: %v", err)
339 t.Errorf("#%d: Got:\n%s\nExpected:\n%s\n", i, s, tc.out)
344 pbStrings := new(pb.Strings)
345 if err := proto.UnmarshalText(s, pbStrings); err != nil {
346 t.Errorf("#%d: UnmarshalText: %v", i, err)
349 if !proto.Equal(pbStrings, tc.in) {
350 t.Errorf("#%d: Round-trip failed:\nstart: %v\n end: %v", i, tc.in, pbStrings)
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 {
363 var outOfSpace = errors.New("proto: insufficient space")
365 func (w *limitedWriter) Write(p []byte) (n int, err error) {
366 var avail = w.limit - w.b.Len()
373 n, _ = w.b.Write(p[:avail])
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)
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)
388 x := text[:buf.limit]
390 t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v===\n", s, x)
395 func TestFloats(t *testing.T) {
402 {math.Inf(1), "inf"},
403 {math.Inf(-1), "-inf"},
406 for _, test := range tests {
407 msg := &pb.FloatingPoint{F: &test.f}
408 got := strings.TrimSpace(msg.String())
409 want := `f:` + test.want
411 t.Errorf("f=%f: got %q, want %q", test.f, got, want)
416 func TestRepeatedNilText(t *testing.T) {
417 m := &pb.MessageList{
418 Message: []*pb.MessageList_Message{
421 Name: proto.String("Horse"),
426 want := `Message <nil>
432 if s := proto.MarshalTextString(m); s != want {
433 t.Errorf(" got: %s\nwant: %s", s, want)
437 func TestProto3Text(t *testing.T) {
443 {&proto3pb.Message{}, ``},
444 // zero message except for an empty byte slice
445 {&proto3pb.Message{Data: []byte{}}, ``},
447 {&proto3pb.Message{Name: "Rob", HeightInCm: 175}, `name:"Rob" height_in_cm:175`},
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).
453 &pb.MessageWithMap{NameMapping: map[int32]string{
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" >`,
464 // map with nil value; not well-defined, but we shouldn't crash
466 &pb.MessageWithMap{MsgMapping: map[int64]*pb.FloatingPoint{7: nil}},
467 `msg_mapping:<key:7 >`,
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)
478 func TestRacyMarshal(t *testing.T) {
479 // This test should be run with the race detector.
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)
487 m := &proto3pb.Message{
490 Anything: &types.Any{TypeUrl: "type.googleapis.com/" + proto.MessageName(any), Value: b},
493 wantText := proto.MarshalTextString(m)
494 wantBytes, err := proto.Marshal(m)
496 t.Fatalf("proto.Marshal error: %v", err)
499 var wg sync.WaitGroup
502 for i := 0; i < 10; i++ {
505 got := proto.MarshalTextString(m)
507 t.Errorf("proto.MarshalTextString = %q, want %q", got, wantText)
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)