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