github.com/matm/etcd@v0.3.1-0.20140328024009-5b4a473f1453/third_party/code.google.com/p/goprotobuf/proto/text_parser_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  	"math"
    36  	"reflect"
    37  	"testing"
    38  
    39  	. "./testdata"
    40  	. "github.com/coreos/etcd/third_party/code.google.com/p/goprotobuf/proto"
    41  )
    42  
    43  type UnmarshalTextTest struct {
    44  	in	string
    45  	err	string	// if "", no error expected
    46  	out	*MyMessage
    47  }
    48  
    49  func buildExtStructTest(text string) UnmarshalTextTest {
    50  	msg := &MyMessage{
    51  		Count: Int32(42),
    52  	}
    53  	SetExtension(msg, E_Ext_More, &Ext{
    54  		Data: String("Hello, world!"),
    55  	})
    56  	return UnmarshalTextTest{in: text, out: msg}
    57  }
    58  
    59  func buildExtDataTest(text string) UnmarshalTextTest {
    60  	msg := &MyMessage{
    61  		Count: Int32(42),
    62  	}
    63  	SetExtension(msg, E_Ext_Text, String("Hello, world!"))
    64  	SetExtension(msg, E_Ext_Number, Int32(1729))
    65  	return UnmarshalTextTest{in: text, out: msg}
    66  }
    67  
    68  func buildExtRepStringTest(text string) UnmarshalTextTest {
    69  	msg := &MyMessage{
    70  		Count: Int32(42),
    71  	}
    72  	if err := SetExtension(msg, E_Greeting, []string{"bula", "hola"}); err != nil {
    73  		panic(err)
    74  	}
    75  	return UnmarshalTextTest{in: text, out: msg}
    76  }
    77  
    78  var unMarshalTextTests = []UnmarshalTextTest{
    79  	// Basic
    80  	{
    81  		in:	" count:42\n  name:\"Dave\" ",
    82  		out: &MyMessage{
    83  			Count:	Int32(42),
    84  			Name:	String("Dave"),
    85  		},
    86  	},
    87  
    88  	// Empty quoted string
    89  	{
    90  		in:	`count:42 name:""`,
    91  		out: &MyMessage{
    92  			Count:	Int32(42),
    93  			Name:	String(""),
    94  		},
    95  	},
    96  
    97  	// Quoted string concatenation
    98  	{
    99  		in:	`count:42 name: "My name is "` + "\n" + `"elsewhere"`,
   100  		out: &MyMessage{
   101  			Count:	Int32(42),
   102  			Name:	String("My name is elsewhere"),
   103  		},
   104  	},
   105  
   106  	// Quoted string with escaped apostrophe
   107  	{
   108  		in:	`count:42 name: "HOLIDAY - New Year\'s Day"`,
   109  		out: &MyMessage{
   110  			Count:	Int32(42),
   111  			Name:	String("HOLIDAY - New Year's Day"),
   112  		},
   113  	},
   114  
   115  	// Quoted string with single quote
   116  	{
   117  		in:	`count:42 name: 'Roger "The Ramster" Ramjet'`,
   118  		out: &MyMessage{
   119  			Count:	Int32(42),
   120  			Name:	String(`Roger "The Ramster" Ramjet`),
   121  		},
   122  	},
   123  
   124  	// Quoted string with all the accepted special characters from the C++ test
   125  	{
   126  		in:	`count:42 name: ` + "\"\\\"A string with \\' characters \\n and \\r newlines and \\t tabs and \\001 slashes \\\\ and  multiple   spaces\"",
   127  		out: &MyMessage{
   128  			Count:	Int32(42),
   129  			Name:	String("\"A string with ' characters \n and \r newlines and \t tabs and \001 slashes \\ and  multiple   spaces"),
   130  		},
   131  	},
   132  
   133  	// Quoted string with quoted backslash
   134  	{
   135  		in:	`count:42 name: "\\'xyz"`,
   136  		out: &MyMessage{
   137  			Count:	Int32(42),
   138  			Name:	String(`\'xyz`),
   139  		},
   140  	},
   141  
   142  	// Quoted string with UTF-8 bytes.
   143  	{
   144  		in:	"count:42 name: '\303\277\302\201\xAB'",
   145  		out: &MyMessage{
   146  			Count:	Int32(42),
   147  			Name:	String("\303\277\302\201\xAB"),
   148  		},
   149  	},
   150  
   151  	// Bad quoted string
   152  	{
   153  		in:	`inner: < host: "\0" >` + "\n",
   154  		err:	`line 1.15: invalid quoted string "\0"`,
   155  	},
   156  
   157  	// Number too large for int64
   158  	{
   159  		in:	"count: 123456789012345678901",
   160  		err:	"line 1.7: invalid int32: 123456789012345678901",
   161  	},
   162  
   163  	// Number too large for int32
   164  	{
   165  		in:	"count: 1234567890123",
   166  		err:	"line 1.7: invalid int32: 1234567890123",
   167  	},
   168  
   169  	// Number in hexadecimal
   170  	{
   171  		in:	"count: 0x2beef",
   172  		out: &MyMessage{
   173  			Count: Int32(0x2beef),
   174  		},
   175  	},
   176  
   177  	// Number in octal
   178  	{
   179  		in:	"count: 024601",
   180  		out: &MyMessage{
   181  			Count: Int32(024601),
   182  		},
   183  	},
   184  
   185  	// Floating point number with "f" suffix
   186  	{
   187  		in:	"count: 4 others:< weight: 17.0f >",
   188  		out: &MyMessage{
   189  			Count:	Int32(4),
   190  			Others: []*OtherMessage{
   191  				{
   192  					Weight: Float32(17),
   193  				},
   194  			},
   195  		},
   196  	},
   197  
   198  	// Floating point positive infinity
   199  	{
   200  		in:	"count: 4 bigfloat: inf",
   201  		out: &MyMessage{
   202  			Count:		Int32(4),
   203  			Bigfloat:	Float64(math.Inf(1)),
   204  		},
   205  	},
   206  
   207  	// Floating point negative infinity
   208  	{
   209  		in:	"count: 4 bigfloat: -inf",
   210  		out: &MyMessage{
   211  			Count:		Int32(4),
   212  			Bigfloat:	Float64(math.Inf(-1)),
   213  		},
   214  	},
   215  
   216  	// Number too large for float32
   217  	{
   218  		in:	"others:< weight: 12345678901234567890123456789012345678901234567890 >",
   219  		err:	"line 1.17: invalid float32: 12345678901234567890123456789012345678901234567890",
   220  	},
   221  
   222  	// Number posing as a quoted string
   223  	{
   224  		in:	`inner: < host: 12 >` + "\n",
   225  		err:	`line 1.15: invalid string: 12`,
   226  	},
   227  
   228  	// Quoted string posing as int32
   229  	{
   230  		in:	`count: "12"`,
   231  		err:	`line 1.7: invalid int32: "12"`,
   232  	},
   233  
   234  	// Quoted string posing a float32
   235  	{
   236  		in:	`others:< weight: "17.4" >`,
   237  		err:	`line 1.17: invalid float32: "17.4"`,
   238  	},
   239  
   240  	// Enum
   241  	{
   242  		in:	`count:42 bikeshed: BLUE`,
   243  		out: &MyMessage{
   244  			Count:		Int32(42),
   245  			Bikeshed:	MyMessage_BLUE.Enum(),
   246  		},
   247  	},
   248  
   249  	// Repeated field
   250  	{
   251  		in:	`count:42 pet: "horsey" pet:"bunny"`,
   252  		out: &MyMessage{
   253  			Count:	Int32(42),
   254  			Pet:	[]string{"horsey", "bunny"},
   255  		},
   256  	},
   257  
   258  	// Repeated message with/without colon and <>/{}
   259  	{
   260  		in:	`count:42 others:{} others{} others:<> others:{}`,
   261  		out: &MyMessage{
   262  			Count:	Int32(42),
   263  			Others: []*OtherMessage{
   264  				{},
   265  				{},
   266  				{},
   267  				{},
   268  			},
   269  		},
   270  	},
   271  
   272  	// Missing colon for inner message
   273  	{
   274  		in:	`count:42 inner < host: "cauchy.syd" >`,
   275  		out: &MyMessage{
   276  			Count:	Int32(42),
   277  			Inner: &InnerMessage{
   278  				Host: String("cauchy.syd"),
   279  			},
   280  		},
   281  	},
   282  
   283  	// Missing colon for string field
   284  	{
   285  		in:	`name "Dave"`,
   286  		err:	`line 1.5: expected ':', found "\"Dave\""`,
   287  	},
   288  
   289  	// Missing colon for int32 field
   290  	{
   291  		in:	`count 42`,
   292  		err:	`line 1.6: expected ':', found "42"`,
   293  	},
   294  
   295  	// Missing required field
   296  	{
   297  		in:	``,
   298  		err:	`line 1.0: message testdata.MyMessage missing required field "count"`,
   299  	},
   300  
   301  	// Repeated non-repeated field
   302  	{
   303  		in:	`name: "Rob" name: "Russ"`,
   304  		err:	`line 1.12: non-repeated field "name" was repeated`,
   305  	},
   306  
   307  	// Group
   308  	{
   309  		in:	`count: 17 SomeGroup { group_field: 12 }`,
   310  		out: &MyMessage{
   311  			Count:	Int32(17),
   312  			Somegroup: &MyMessage_SomeGroup{
   313  				GroupField: Int32(12),
   314  			},
   315  		},
   316  	},
   317  
   318  	// Semicolon between fields
   319  	{
   320  		in:	`count:3;name:"Calvin"`,
   321  		out: &MyMessage{
   322  			Count:	Int32(3),
   323  			Name:	String("Calvin"),
   324  		},
   325  	},
   326  	// Comma between fields
   327  	{
   328  		in:	`count:4,name:"Ezekiel"`,
   329  		out: &MyMessage{
   330  			Count:	Int32(4),
   331  			Name:	String("Ezekiel"),
   332  		},
   333  	},
   334  
   335  	// Extension
   336  	buildExtStructTest(`count: 42 [testdata.Ext.more]:<data:"Hello, world!" >`),
   337  	buildExtStructTest(`count: 42 [testdata.Ext.more] {data:"Hello, world!"}`),
   338  	buildExtDataTest(`count: 42 [testdata.Ext.text]:"Hello, world!" [testdata.Ext.number]:1729`),
   339  	buildExtRepStringTest(`count: 42 [testdata.greeting]:"bula" [testdata.greeting]:"hola"`),
   340  
   341  	// Big all-in-one
   342  	{
   343  		in: "count:42  # Meaning\n" +
   344  			`name:"Dave" ` +
   345  			`quote:"\"I didn't want to go.\"" ` +
   346  			`pet:"bunny" ` +
   347  			`pet:"kitty" ` +
   348  			`pet:"horsey" ` +
   349  			`inner:<` +
   350  			`  host:"footrest.syd" ` +
   351  			`  port:7001 ` +
   352  			`  connected:true ` +
   353  			`> ` +
   354  			`others:<` +
   355  			`  key:3735928559 ` +
   356  			`  value:"\x01A\a\f" ` +
   357  			`> ` +
   358  			`others:<` +
   359  			"  weight:58.9  # Atomic weight of Co\n" +
   360  			`  inner:<` +
   361  			`    host:"lesha.mtv" ` +
   362  			`    port:8002 ` +
   363  			`  >` +
   364  			`>`,
   365  		out: &MyMessage{
   366  			Count:	Int32(42),
   367  			Name:	String("Dave"),
   368  			Quote:	String(`"I didn't want to go."`),
   369  			Pet:	[]string{"bunny", "kitty", "horsey"},
   370  			Inner: &InnerMessage{
   371  				Host:		String("footrest.syd"),
   372  				Port:		Int32(7001),
   373  				Connected:	Bool(true),
   374  			},
   375  			Others: []*OtherMessage{
   376  				{
   377  					Key:	Int64(3735928559),
   378  					Value:	[]byte{0x1, 'A', '\a', '\f'},
   379  				},
   380  				{
   381  					Weight:	Float32(58.9),
   382  					Inner: &InnerMessage{
   383  						Host:	String("lesha.mtv"),
   384  						Port:	Int32(8002),
   385  					},
   386  				},
   387  			},
   388  		},
   389  	},
   390  }
   391  
   392  func TestUnmarshalText(t *testing.T) {
   393  	for i, test := range unMarshalTextTests {
   394  		pb := new(MyMessage)
   395  		err := UnmarshalText(test.in, pb)
   396  		if test.err == "" {
   397  			// We don't expect failure.
   398  			if err != nil {
   399  				t.Errorf("Test %d: Unexpected error: %v", i, err)
   400  			} else if !reflect.DeepEqual(pb, test.out) {
   401  				t.Errorf("Test %d: Incorrect populated \nHave: %v\nWant: %v",
   402  					i, pb, test.out)
   403  			}
   404  		} else {
   405  			// We do expect failure.
   406  			if err == nil {
   407  				t.Errorf("Test %d: Didn't get expected error: %v", i, test.err)
   408  			} else if err.Error() != test.err {
   409  				t.Errorf("Test %d: Incorrect error.\nHave: %v\nWant: %v",
   410  					i, err.Error(), test.err)
   411  			}
   412  		}
   413  	}
   414  }
   415  
   416  // Regression test; this caused a panic.
   417  func TestRepeatedEnum(t *testing.T) {
   418  	pb := new(RepeatedEnum)
   419  	if err := UnmarshalText("color: RED", pb); err != nil {
   420  		t.Fatal(err)
   421  	}
   422  	exp := &RepeatedEnum{
   423  		Color: []RepeatedEnum_Color{RepeatedEnum_RED},
   424  	}
   425  	if !Equal(pb, exp) {
   426  		t.Errorf("Incorrect populated \nHave: %v\nWant: %v", pb, exp)
   427  	}
   428  }
   429  
   430  var benchInput string
   431  
   432  func init() {
   433  	benchInput = "count: 4\n"
   434  	for i := 0; i < 1000; i++ {
   435  		benchInput += "pet: \"fido\"\n"
   436  	}
   437  
   438  	// Check it is valid input.
   439  	pb := new(MyMessage)
   440  	err := UnmarshalText(benchInput, pb)
   441  	if err != nil {
   442  		panic("Bad benchmark input: " + err.Error())
   443  	}
   444  }
   445  
   446  func BenchmarkUnmarshalText(b *testing.B) {
   447  	pb := new(MyMessage)
   448  	for i := 0; i < b.N; i++ {
   449  		UnmarshalText(benchInput, pb)
   450  	}
   451  	b.SetBytes(int64(len(benchInput)))
   452  }