github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/golang/protobuf/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  // https://yougam/libraries/golang/protobuf
     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  	. "github.com/insionng/yougam/libraries/golang/protobuf/proto"
    40  	proto3pb "github.com/insionng/yougam/libraries/golang/protobuf/proto/proto3_proto"
    41  	. "github.com/insionng/yougam/libraries/golang/protobuf/proto/testdata"
    42  )
    43  
    44  type UnmarshalTextTest struct {
    45  	in  string
    46  	err string // if "", no error expected
    47  	out *MyMessage
    48  }
    49  
    50  func buildExtStructTest(text string) UnmarshalTextTest {
    51  	msg := &MyMessage{
    52  		Count: Int32(42),
    53  	}
    54  	SetExtension(msg, E_Ext_More, &Ext{
    55  		Data: String("Hello, world!"),
    56  	})
    57  	return UnmarshalTextTest{in: text, out: msg}
    58  }
    59  
    60  func buildExtDataTest(text string) UnmarshalTextTest {
    61  	msg := &MyMessage{
    62  		Count: Int32(42),
    63  	}
    64  	SetExtension(msg, E_Ext_Text, String("Hello, world!"))
    65  	SetExtension(msg, E_Ext_Number, Int32(1729))
    66  	return UnmarshalTextTest{in: text, out: msg}
    67  }
    68  
    69  func buildExtRepStringTest(text string) UnmarshalTextTest {
    70  	msg := &MyMessage{
    71  		Count: Int32(42),
    72  	}
    73  	if err := SetExtension(msg, E_Greeting, []string{"bula", "hola"}); err != nil {
    74  		panic(err)
    75  	}
    76  	return UnmarshalTextTest{in: text, out: msg}
    77  }
    78  
    79  var unMarshalTextTests = []UnmarshalTextTest{
    80  	// Basic
    81  	{
    82  		in: " count:42\n  name:\"Dave\" ",
    83  		out: &MyMessage{
    84  			Count: Int32(42),
    85  			Name:  String("Dave"),
    86  		},
    87  	},
    88  
    89  	// Empty quoted string
    90  	{
    91  		in: `count:42 name:""`,
    92  		out: &MyMessage{
    93  			Count: Int32(42),
    94  			Name:  String(""),
    95  		},
    96  	},
    97  
    98  	// Quoted string concatenation with double quotes
    99  	{
   100  		in: `count:42 name: "My name is "` + "\n" + `"elsewhere"`,
   101  		out: &MyMessage{
   102  			Count: Int32(42),
   103  			Name:  String("My name is elsewhere"),
   104  		},
   105  	},
   106  
   107  	// Quoted string concatenation with single quotes
   108  	{
   109  		in: "count:42 name: 'My name is '\n'elsewhere'",
   110  		out: &MyMessage{
   111  			Count: Int32(42),
   112  			Name:  String("My name is elsewhere"),
   113  		},
   114  	},
   115  
   116  	// Quoted string concatenations with mixed quotes
   117  	{
   118  		in: "count:42 name: 'My name is '\n\"elsewhere\"",
   119  		out: &MyMessage{
   120  			Count: Int32(42),
   121  			Name:  String("My name is elsewhere"),
   122  		},
   123  	},
   124  	{
   125  		in: "count:42 name: \"My name is \"\n'elsewhere'",
   126  		out: &MyMessage{
   127  			Count: Int32(42),
   128  			Name:  String("My name is elsewhere"),
   129  		},
   130  	},
   131  
   132  	// Quoted string with escaped apostrophe
   133  	{
   134  		in: `count:42 name: "HOLIDAY - New Year\'s Day"`,
   135  		out: &MyMessage{
   136  			Count: Int32(42),
   137  			Name:  String("HOLIDAY - New Year's Day"),
   138  		},
   139  	},
   140  
   141  	// Quoted string with single quote
   142  	{
   143  		in: `count:42 name: 'Roger "The Ramster" Ramjet'`,
   144  		out: &MyMessage{
   145  			Count: Int32(42),
   146  			Name:  String(`Roger "The Ramster" Ramjet`),
   147  		},
   148  	},
   149  
   150  	// Quoted string with all the accepted special characters from the C++ test
   151  	{
   152  		in: `count:42 name: ` + "\"\\\"A string with \\' characters \\n and \\r newlines and \\t tabs and \\001 slashes \\\\ and  multiple   spaces\"",
   153  		out: &MyMessage{
   154  			Count: Int32(42),
   155  			Name:  String("\"A string with ' characters \n and \r newlines and \t tabs and \001 slashes \\ and  multiple   spaces"),
   156  		},
   157  	},
   158  
   159  	// Quoted string with quoted backslash
   160  	{
   161  		in: `count:42 name: "\\'xyz"`,
   162  		out: &MyMessage{
   163  			Count: Int32(42),
   164  			Name:  String(`\'xyz`),
   165  		},
   166  	},
   167  
   168  	// Quoted string with UTF-8 bytes.
   169  	{
   170  		in: "count:42 name: '\303\277\302\201\xAB'",
   171  		out: &MyMessage{
   172  			Count: Int32(42),
   173  			Name:  String("\303\277\302\201\xAB"),
   174  		},
   175  	},
   176  
   177  	// Bad quoted string
   178  	{
   179  		in:  `inner: < host: "\0" >` + "\n",
   180  		err: `line 1.15: invalid quoted string "\0": \0 requires 2 following digits`,
   181  	},
   182  
   183  	// Number too large for int64
   184  	{
   185  		in:  "count: 1 others { key: 123456789012345678901 }",
   186  		err: "line 1.23: invalid int64: 123456789012345678901",
   187  	},
   188  
   189  	// Number too large for int32
   190  	{
   191  		in:  "count: 1234567890123",
   192  		err: "line 1.7: invalid int32: 1234567890123",
   193  	},
   194  
   195  	// Number in hexadecimal
   196  	{
   197  		in: "count: 0x2beef",
   198  		out: &MyMessage{
   199  			Count: Int32(0x2beef),
   200  		},
   201  	},
   202  
   203  	// Number in octal
   204  	{
   205  		in: "count: 024601",
   206  		out: &MyMessage{
   207  			Count: Int32(024601),
   208  		},
   209  	},
   210  
   211  	// Floating point number with "f" suffix
   212  	{
   213  		in: "count: 4 others:< weight: 17.0f >",
   214  		out: &MyMessage{
   215  			Count: Int32(4),
   216  			Others: []*OtherMessage{
   217  				{
   218  					Weight: Float32(17),
   219  				},
   220  			},
   221  		},
   222  	},
   223  
   224  	// Floating point positive infinity
   225  	{
   226  		in: "count: 4 bigfloat: inf",
   227  		out: &MyMessage{
   228  			Count:    Int32(4),
   229  			Bigfloat: Float64(math.Inf(1)),
   230  		},
   231  	},
   232  
   233  	// Floating point negative infinity
   234  	{
   235  		in: "count: 4 bigfloat: -inf",
   236  		out: &MyMessage{
   237  			Count:    Int32(4),
   238  			Bigfloat: Float64(math.Inf(-1)),
   239  		},
   240  	},
   241  
   242  	// Number too large for float32
   243  	{
   244  		in:  "others:< weight: 12345678901234567890123456789012345678901234567890 >",
   245  		err: "line 1.17: invalid float32: 12345678901234567890123456789012345678901234567890",
   246  	},
   247  
   248  	// Number posing as a quoted string
   249  	{
   250  		in:  `inner: < host: 12 >` + "\n",
   251  		err: `line 1.15: invalid string: 12`,
   252  	},
   253  
   254  	// Quoted string posing as int32
   255  	{
   256  		in:  `count: "12"`,
   257  		err: `line 1.7: invalid int32: "12"`,
   258  	},
   259  
   260  	// Quoted string posing a float32
   261  	{
   262  		in:  `others:< weight: "17.4" >`,
   263  		err: `line 1.17: invalid float32: "17.4"`,
   264  	},
   265  
   266  	// Enum
   267  	{
   268  		in: `count:42 bikeshed: BLUE`,
   269  		out: &MyMessage{
   270  			Count:    Int32(42),
   271  			Bikeshed: MyMessage_BLUE.Enum(),
   272  		},
   273  	},
   274  
   275  	// Repeated field
   276  	{
   277  		in: `count:42 pet: "horsey" pet:"bunny"`,
   278  		out: &MyMessage{
   279  			Count: Int32(42),
   280  			Pet:   []string{"horsey", "bunny"},
   281  		},
   282  	},
   283  
   284  	// Repeated field with list notation
   285  	{
   286  		in: `count:42 pet: ["horsey", "bunny"]`,
   287  		out: &MyMessage{
   288  			Count: Int32(42),
   289  			Pet:   []string{"horsey", "bunny"},
   290  		},
   291  	},
   292  
   293  	// Repeated message with/without colon and <>/{}
   294  	{
   295  		in: `count:42 others:{} others{} others:<> others:{}`,
   296  		out: &MyMessage{
   297  			Count: Int32(42),
   298  			Others: []*OtherMessage{
   299  				{},
   300  				{},
   301  				{},
   302  				{},
   303  			},
   304  		},
   305  	},
   306  
   307  	// Missing colon for inner message
   308  	{
   309  		in: `count:42 inner < host: "cauchy.syd" >`,
   310  		out: &MyMessage{
   311  			Count: Int32(42),
   312  			Inner: &InnerMessage{
   313  				Host: String("cauchy.syd"),
   314  			},
   315  		},
   316  	},
   317  
   318  	// Missing colon for string field
   319  	{
   320  		in:  `name "Dave"`,
   321  		err: `line 1.5: expected ':', found "\"Dave\""`,
   322  	},
   323  
   324  	// Missing colon for int32 field
   325  	{
   326  		in:  `count 42`,
   327  		err: `line 1.6: expected ':', found "42"`,
   328  	},
   329  
   330  	// Missing required field
   331  	{
   332  		in:  `name: "Pawel"`,
   333  		err: `proto: required field "testdata.MyMessage.count" not set`,
   334  		out: &MyMessage{
   335  			Name: String("Pawel"),
   336  		},
   337  	},
   338  
   339  	// Missing required field in a required submessage
   340  	{
   341  		in:  `count: 42 we_must_go_deeper < leo_finally_won_an_oscar <> >`,
   342  		err: `proto: required field "testdata.InnerMessage.host" not set`,
   343  		out: &MyMessage{
   344  			Count:          Int32(42),
   345  			WeMustGoDeeper: &RequiredInnerMessage{LeoFinallyWonAnOscar: &InnerMessage{}},
   346  		},
   347  	},
   348  
   349  	// Repeated non-repeated field
   350  	{
   351  		in:  `name: "Rob" name: "Russ"`,
   352  		err: `line 1.12: non-repeated field "name" was repeated`,
   353  	},
   354  
   355  	// Group
   356  	{
   357  		in: `count: 17 SomeGroup { group_field: 12 }`,
   358  		out: &MyMessage{
   359  			Count: Int32(17),
   360  			Somegroup: &MyMessage_SomeGroup{
   361  				GroupField: Int32(12),
   362  			},
   363  		},
   364  	},
   365  
   366  	// Semicolon between fields
   367  	{
   368  		in: `count:3;name:"Calvin"`,
   369  		out: &MyMessage{
   370  			Count: Int32(3),
   371  			Name:  String("Calvin"),
   372  		},
   373  	},
   374  	// Comma between fields
   375  	{
   376  		in: `count:4,name:"Ezekiel"`,
   377  		out: &MyMessage{
   378  			Count: Int32(4),
   379  			Name:  String("Ezekiel"),
   380  		},
   381  	},
   382  
   383  	// Extension
   384  	buildExtStructTest(`count: 42 [testdata.Ext.more]:<data:"Hello, world!" >`),
   385  	buildExtStructTest(`count: 42 [testdata.Ext.more] {data:"Hello, world!"}`),
   386  	buildExtDataTest(`count: 42 [testdata.Ext.text]:"Hello, world!" [testdata.Ext.number]:1729`),
   387  	buildExtRepStringTest(`count: 42 [testdata.greeting]:"bula" [testdata.greeting]:"hola"`),
   388  
   389  	// Big all-in-one
   390  	{
   391  		in: "count:42  # Meaning\n" +
   392  			`name:"Dave" ` +
   393  			`quote:"\"I didn't want to go.\"" ` +
   394  			`pet:"bunny" ` +
   395  			`pet:"kitty" ` +
   396  			`pet:"horsey" ` +
   397  			`inner:<` +
   398  			`  host:"footrest.syd" ` +
   399  			`  port:7001 ` +
   400  			`  connected:true ` +
   401  			`> ` +
   402  			`others:<` +
   403  			`  key:3735928559 ` +
   404  			`  value:"\x01A\a\f" ` +
   405  			`> ` +
   406  			`others:<` +
   407  			"  weight:58.9  # Atomic weight of Co\n" +
   408  			`  inner:<` +
   409  			`    host:"lesha.mtv" ` +
   410  			`    port:8002 ` +
   411  			`  >` +
   412  			`>`,
   413  		out: &MyMessage{
   414  			Count: Int32(42),
   415  			Name:  String("Dave"),
   416  			Quote: String(`"I didn't want to go."`),
   417  			Pet:   []string{"bunny", "kitty", "horsey"},
   418  			Inner: &InnerMessage{
   419  				Host:      String("footrest.syd"),
   420  				Port:      Int32(7001),
   421  				Connected: Bool(true),
   422  			},
   423  			Others: []*OtherMessage{
   424  				{
   425  					Key:   Int64(3735928559),
   426  					Value: []byte{0x1, 'A', '\a', '\f'},
   427  				},
   428  				{
   429  					Weight: Float32(58.9),
   430  					Inner: &InnerMessage{
   431  						Host: String("lesha.mtv"),
   432  						Port: Int32(8002),
   433  					},
   434  				},
   435  			},
   436  		},
   437  	},
   438  }
   439  
   440  func TestUnmarshalText(t *testing.T) {
   441  	for i, test := range unMarshalTextTests {
   442  		pb := new(MyMessage)
   443  		err := UnmarshalText(test.in, pb)
   444  		if test.err == "" {
   445  			// We don't expect failure.
   446  			if err != nil {
   447  				t.Errorf("Test %d: Unexpected error: %v", i, err)
   448  			} else if !reflect.DeepEqual(pb, test.out) {
   449  				t.Errorf("Test %d: Incorrect populated \nHave: %v\nWant: %v",
   450  					i, pb, test.out)
   451  			}
   452  		} else {
   453  			// We do expect failure.
   454  			if err == nil {
   455  				t.Errorf("Test %d: Didn't get expected error: %v", i, test.err)
   456  			} else if err.Error() != test.err {
   457  				t.Errorf("Test %d: Incorrect error.\nHave: %v\nWant: %v",
   458  					i, err.Error(), test.err)
   459  			} else if _, ok := err.(*RequiredNotSetError); ok && test.out != nil && !reflect.DeepEqual(pb, test.out) {
   460  				t.Errorf("Test %d: Incorrect populated \nHave: %v\nWant: %v",
   461  					i, pb, test.out)
   462  			}
   463  		}
   464  	}
   465  }
   466  
   467  func TestUnmarshalTextCustomMessage(t *testing.T) {
   468  	msg := &textMessage{}
   469  	if err := UnmarshalText("custom", msg); err != nil {
   470  		t.Errorf("Unexpected error from custom unmarshal: %v", err)
   471  	}
   472  	if UnmarshalText("not custom", msg) == nil {
   473  		t.Errorf("Didn't get expected error from custom unmarshal")
   474  	}
   475  }
   476  
   477  // Regression test; this caused a panic.
   478  func TestRepeatedEnum(t *testing.T) {
   479  	pb := new(RepeatedEnum)
   480  	if err := UnmarshalText("color: RED", pb); err != nil {
   481  		t.Fatal(err)
   482  	}
   483  	exp := &RepeatedEnum{
   484  		Color: []RepeatedEnum_Color{RepeatedEnum_RED},
   485  	}
   486  	if !Equal(pb, exp) {
   487  		t.Errorf("Incorrect populated \nHave: %v\nWant: %v", pb, exp)
   488  	}
   489  }
   490  
   491  func TestProto3TextParsing(t *testing.T) {
   492  	m := new(proto3pb.Message)
   493  	const in = `name: "Wallace" true_scotsman: true`
   494  	want := &proto3pb.Message{
   495  		Name:         "Wallace",
   496  		TrueScotsman: true,
   497  	}
   498  	if err := UnmarshalText(in, m); err != nil {
   499  		t.Fatal(err)
   500  	}
   501  	if !Equal(m, want) {
   502  		t.Errorf("\n got %v\nwant %v", m, want)
   503  	}
   504  }
   505  
   506  func TestMapParsing(t *testing.T) {
   507  	m := new(MessageWithMap)
   508  	const in = `name_mapping:<key:1234 value:"Feist"> name_mapping:<key:1 value:"Beatles">` +
   509  		`msg_mapping:<key:-4, value:<f: 2.0>,>` + // separating commas are okay
   510  		`msg_mapping<key:-2 value<f: 4.0>>` + // no colon after "value"
   511  		`byte_mapping:<key:true value:"so be it">`
   512  	want := &MessageWithMap{
   513  		NameMapping: map[int32]string{
   514  			1:    "Beatles",
   515  			1234: "Feist",
   516  		},
   517  		MsgMapping: map[int64]*FloatingPoint{
   518  			-4: {F: Float64(2.0)},
   519  			-2: {F: Float64(4.0)},
   520  		},
   521  		ByteMapping: map[bool][]byte{
   522  			true: []byte("so be it"),
   523  		},
   524  	}
   525  	if err := UnmarshalText(in, m); err != nil {
   526  		t.Fatal(err)
   527  	}
   528  	if !Equal(m, want) {
   529  		t.Errorf("\n got %v\nwant %v", m, want)
   530  	}
   531  }
   532  
   533  func TestOneofParsing(t *testing.T) {
   534  	const in = `name:"Shrek"`
   535  	m := new(Communique)
   536  	want := &Communique{Union: &Communique_Name{"Shrek"}}
   537  	if err := UnmarshalText(in, m); err != nil {
   538  		t.Fatal(err)
   539  	}
   540  	if !Equal(m, want) {
   541  		t.Errorf("\n got %v\nwant %v", m, want)
   542  	}
   543  }
   544  
   545  var benchInput string
   546  
   547  func init() {
   548  	benchInput = "count: 4\n"
   549  	for i := 0; i < 1000; i++ {
   550  		benchInput += "pet: \"fido\"\n"
   551  	}
   552  
   553  	// Check it is valid input.
   554  	pb := new(MyMessage)
   555  	err := UnmarshalText(benchInput, pb)
   556  	if err != nil {
   557  		panic("Bad benchmark input: " + err.Error())
   558  	}
   559  }
   560  
   561  func BenchmarkUnmarshalText(b *testing.B) {
   562  	pb := new(MyMessage)
   563  	for i := 0; i < b.N; i++ {
   564  		UnmarshalText(benchInput, pb)
   565  	}
   566  	b.SetBytes(int64(len(benchInput)))
   567  }