github.com/schwarzm/garden-linux@v0.0.0-20150507151835-33bca2147c47/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto/text_test.go (about)

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