...

Source file src/github.com/emicklei/proto/option_test.go

Documentation: github.com/emicklei/proto

     1  // Copyright (c) 2017 Ernest Micklei
     2  //
     3  // MIT License
     4  //
     5  // Permission is hereby granted, free of charge, to any person obtaining
     6  // a copy of this software and associated documentation files (the
     7  // "Software"), to deal in the Software without restriction, including
     8  // without limitation the rights to use, copy, modify, merge, publish,
     9  // distribute, sublicense, and/or sell copies of the Software, and to
    10  // permit persons to whom the Software is furnished to do so, subject to
    11  // the following conditions:
    12  //
    13  // The above copyright notice and this permission notice shall be
    14  // included in all copies or substantial portions of the Software.
    15  //
    16  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    17  // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    18  // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    19  // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
    20  // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    21  // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
    22  // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    23  
    24  package proto
    25  
    26  import (
    27  	"testing"
    28  )
    29  
    30  func TestOptionCases(t *testing.T) {
    31  	for i, each := range []struct {
    32  		proto     string
    33  		name      string
    34  		strLit    string
    35  		nonStrLit string
    36  	}{{
    37  		`option (full).java_package = "com.example.foo";`,
    38  		"(full).java_package",
    39  		"com.example.foo",
    40  		"",
    41  	}, {
    42  		`option Bool = true;`,
    43  		"Bool",
    44  		"",
    45  		"true",
    46  	}, {
    47  		`option Float = -3.14E1;`,
    48  		"Float",
    49  		"",
    50  		"-3.14E1",
    51  	}, {
    52  		`option (foo_options) = { opt1: 123 opt2: "baz" };`,
    53  		"(foo_options)",
    54  		"",
    55  		"",
    56  	}, {
    57  		`option foo = []`,
    58  		"foo",
    59  		"",
    60  		"",
    61  	}, {
    62  		`option optimize_for = SPEED;`,
    63  		"optimize_for",
    64  		"",
    65  		"SPEED",
    66  	}, {
    67  		"option (my.enum.service.is.like).rpc = 1;",
    68  		"(my.enum.service.is.like).rpc",
    69  		"",
    70  		"1",
    71  	}, {
    72  		`option (imported.oss.package).action = "literal-double-quotes";`,
    73  		"(imported.oss.package).action",
    74  		"literal-double-quotes",
    75  		"",
    76  	}, {
    77  		`option (imported.oss.package).action = "key:\"literal-double-quotes-escaped\"";`,
    78  		"(imported.oss.package).action",
    79  		`key:\"literal-double-quotes-escaped\"`,
    80  		"",
    81  	}, {
    82  		`option (imported.oss.package).action = 'literalsinglequotes';`,
    83  		"(imported.oss.package).action",
    84  		"literalsinglequotes",
    85  		"",
    86  	}, {
    87  		`option (imported.oss.package).action = 'single-quotes.with/symbols';`,
    88  		"(imported.oss.package).action",
    89  		"single-quotes.with/symbols",
    90  		"",
    91  	}} {
    92  		p := newParserOn(each.proto)
    93  		pr, err := p.Parse()
    94  		if err != nil {
    95  			t.Fatal("testcase failed:", i, err)
    96  		}
    97  		if got, want := len(pr.Elements), 1; got != want {
    98  			t.Fatalf("[%d] got [%v] want [%v]", i, got, want)
    99  		}
   100  		o := pr.Elements[0].(*Option)
   101  		if got, want := o.Name, each.name; got != want {
   102  			t.Errorf("[%d] got [%v] want [%v]", i, got, want)
   103  		}
   104  		if len(each.strLit) > 0 {
   105  			if got, want := o.Constant.Source, each.strLit; got != want {
   106  				t.Errorf("[%d] got [%v] want [%v]", i, got, want)
   107  			}
   108  		}
   109  		if len(each.nonStrLit) > 0 {
   110  			if got, want := o.Constant.Source, each.nonStrLit; got != want {
   111  				t.Errorf("[%d] got [%v] want [%v]", i, got, want)
   112  			}
   113  		}
   114  		if got, want := o.IsEmbedded, false; got != want {
   115  			t.Errorf("[%d] got [%v] want [%v]", i, got, want)
   116  		}
   117  	}
   118  }
   119  
   120  func TestLiteralString(t *testing.T) {
   121  	proto := `"string"`
   122  	p := newParserOn(proto)
   123  	l := new(Literal)
   124  	if err := l.parse(p); err != nil {
   125  		t.Fatal(err)
   126  	}
   127  	if got, want := l.IsString, true; got != want {
   128  		t.Errorf("got [%v] want [%v]", got, want)
   129  	}
   130  	if got, want := l.Source, "string"; got != want {
   131  		t.Errorf("got [%v] want [%v]", got, want)
   132  	}
   133  }
   134  
   135  func TestOptionComments(t *testing.T) {
   136  	proto := `
   137  // comment
   138  option Help = "me"; // inline`
   139  	p := newParserOn(proto)
   140  	pr, err := p.Parse()
   141  	if err != nil {
   142  		t.Fatal(err)
   143  	}
   144  	o := pr.Elements[0].(*Option)
   145  	if got, want := o.IsEmbedded, false; got != want {
   146  		t.Errorf("got [%v] want [%v]", got, want)
   147  	}
   148  	if got, want := o.Comment != nil, true; got != want {
   149  		t.Fatalf("got [%v] want [%v]", got, want)
   150  	}
   151  	if got, want := o.Comment.Lines[0], " comment"; got != want {
   152  		t.Fatalf("got [%v] want [%v]", got, want)
   153  	}
   154  	if got, want := o.InlineComment != nil, true; got != want {
   155  		t.Fatalf("got [%v] want [%v]", got, want)
   156  	}
   157  	if got, want := o.InlineComment.Lines[0], " inline"; got != want {
   158  		t.Fatalf("got [%v] want [%v]", got, want)
   159  	}
   160  	if got, want := o.Position.Line, 3; got != want {
   161  		t.Fatalf("got [%v] want [%v]", got, want)
   162  	}
   163  	if got, want := o.Comment.Position.Line, 2; got != want {
   164  		t.Fatalf("got [%v] want [%v]", got, want)
   165  	}
   166  	if got, want := o.InlineComment.Position.Line, 3; got != want {
   167  		t.Fatalf("got [%v] want [%v]", got, want)
   168  	}
   169  }
   170  
   171  func TestAggregateSyntax(t *testing.T) {
   172  	proto := `
   173  // usage:
   174  message Bar {
   175    // alternative aggregate syntax (uses TextFormat):
   176    int32 b = 2 [(foo_options) = {
   177      opt1: 123,
   178      opt2: "baz"
   179    }];
   180  }
   181  	`
   182  	p := newParserOn(proto)
   183  	pr, err := p.Parse()
   184  	if err != nil {
   185  		t.Fatal(err)
   186  	}
   187  	o := pr.Elements[0].(*Message)
   188  	f := o.Elements[0].(*NormalField)
   189  	if got, want := len(f.Options), 1; got != want {
   190  		t.Fatalf("got [%v] want [%v]", got, want)
   191  	}
   192  	ac := f.Options[0].Constant.Map
   193  	if got, want := len(ac), 2; got != want {
   194  		t.Fatalf("got [%v] want [%v]", got, want)
   195  	}
   196  	if got, want := ac["opt1"].Source, "123"; got != want {
   197  		t.Fatalf("got [%v] want [%v]", got, want)
   198  	}
   199  	if got, want := o.Position.Line, 3; got != want {
   200  		t.Fatalf("got [%v] want [%v]", got, want)
   201  	}
   202  	if got, want := o.Comment.Position.String(), "<input>:2:1"; got != want {
   203  		t.Fatalf("got [%v] want [%v]", got, want)
   204  	}
   205  	if got, want := f.Position.String(), "<input>:5:3"; got != want {
   206  		t.Fatalf("got [%v] want [%v]", got, want)
   207  	}
   208  	if got, want := f.Options[0].Constant.Position.Line, 5; got != want {
   209  		t.Fatalf("got [%v] want [%v]", got, want)
   210  	}
   211  	// check for AggregatedConstants
   212  	list := f.Options[0].AggregatedConstants
   213  	if got, want := list[0].Source, "123"; got != want {
   214  		t.Fatalf("got [%v] want [%v]", got, want)
   215  	}
   216  	if got, want := list[1].Source, "baz"; got != want {
   217  		t.Fatalf("got [%v] want [%v]", got, want)
   218  	}
   219  }
   220  
   221  func TestNonPrimitiveOptionComment(t *testing.T) {
   222  	proto := `
   223  // comment
   224  option Help = { string_field: "value" }; // inline`
   225  	p := newParserOn(proto)
   226  	pr, err := p.Parse()
   227  	if err != nil {
   228  		t.Fatal(err)
   229  	}
   230  	o := pr.Elements[0].(*Option)
   231  	if got, want := o.Comment != nil, true; got != want {
   232  		t.Fatalf("got [%v] want [%v]", got, want)
   233  	}
   234  	if got, want := o.Comment.Lines[0], " comment"; got != want {
   235  		t.Fatalf("got [%v] want [%v]", got, want)
   236  	}
   237  	if got, want := o.InlineComment != nil, true; got != want {
   238  		t.Fatalf("got [%v] want [%v]", got, want)
   239  	}
   240  	if got, want := o.InlineComment.Lines[0], " inline"; got != want {
   241  		t.Fatalf("got [%v] want [%v]", got, want)
   242  	}
   243  }
   244  
   245  func TestFieldCustomOptions(t *testing.T) {
   246  	proto := `foo.bar lots = 1 [foo={hello:1}, bar=2];`
   247  	p := newParserOn(proto)
   248  	f := newNormalField()
   249  	err := f.parse(p)
   250  	if err != nil {
   251  		t.Fatal(err)
   252  	}
   253  	if got, want := f.Type, "foo.bar"; got != want {
   254  		t.Errorf("got [%v] want [%v]", got, want)
   255  	}
   256  	if got, want := f.Name, "lots"; got != want {
   257  		t.Errorf("got [%v] want [%v]", got, want)
   258  	}
   259  	if got, want := len(f.Options), 2; got != want {
   260  		t.Fatalf("got [%v] want [%v]", got, want)
   261  	}
   262  	if got, want := f.Options[0].Name, "foo"; got != want {
   263  		t.Errorf("got [%v] want [%v]", got, want)
   264  	}
   265  	if got, want := f.Options[1].Name, "bar"; got != want {
   266  		t.Errorf("got [%v] want [%v]", got, want)
   267  	}
   268  	if got, want := f.Options[1].Constant.Source, "2"; got != want {
   269  		t.Errorf("got [%v] want [%v]", got, want)
   270  	}
   271  	// check for AggregatedConstants
   272  	if got, want := f.Options[0].AggregatedConstants[0].Name, "hello"; got != want {
   273  		t.Errorf("got [%v] want [%v]", got, want)
   274  	}
   275  	if got, want := f.Options[0].AggregatedConstants[0].PrintsColon, true; got != want {
   276  		t.Errorf("got [%v] want [%v]", got, want)
   277  	}
   278  	if got, want := f.Options[0].AggregatedConstants[0].Source, "1"; got != want {
   279  		t.Errorf("got [%v] want [%v]", got, want)
   280  	}
   281  }
   282  
   283  func TestIgnoreIllegalEscapeCharsInAggregatedConstants(t *testing.T) {
   284  	src := `syntax = "proto3";
   285  	message Person {
   286  	  string name  = 3 [(validate.rules).string = {
   287  						  pattern:   "^[^\d\s]+( [^\d\s]+)*$",
   288  						  max_bytes: 256,
   289  					   }];
   290  	}`
   291  	p := newParserOn(src)
   292  	d, err := p.Parse()
   293  	if err != nil {
   294  		t.Fatal(err)
   295  	}
   296  	f := d.Elements[1].(*Message).Elements[0].(*NormalField)
   297  	if got, want := f.Options[0].Name, "(validate.rules).string"; got != want {
   298  		t.Errorf("got [%v] want [%v]", got, want)
   299  	}
   300  	if got, want := len(f.Options[0].Constant.Map), 2; got != want {
   301  		t.Errorf("got [%v] want [%v]", got, want)
   302  	}
   303  	if got, want := f.Options[0].Constant.Map["pattern"].Source, "^[^\\d\\s]+( [^\\d\\s]+)*$"; got != want {
   304  		t.Errorf("got [%v] want [%v]", got, want)
   305  	}
   306  	if got, want := len(f.Options[0].Constant.OrderedMap), 2; got != want {
   307  		t.Errorf("got [%v] want [%v]", got, want)
   308  	}
   309  	if got, want := f.Options[0].Constant.OrderedMap[0].Name, "pattern"; got != want {
   310  		t.Errorf("got [%v] want [%v]", got, want)
   311  	}
   312  	if got, want := f.Options[0].Constant.OrderedMap[0].Source, "^[^\\d\\s]+( [^\\d\\s]+)*$"; got != want {
   313  		t.Errorf("got [%v] want [%v]", got, want)
   314  	}
   315  	// check for AggregatedConstants
   316  	if got, want := f.Options[0].AggregatedConstants[0].Name, "pattern"; got != want {
   317  		t.Errorf("got [%v] want [%v]", got, want)
   318  	}
   319  	if got, want := f.Options[0].AggregatedConstants[0].PrintsColon, true; got != want {
   320  		t.Errorf("got [%v] want [%v]", got, want)
   321  	}
   322  	if got, want := f.Options[0].AggregatedConstants[0].Source, "^[^\\d\\s]+( [^\\d\\s]+)*$"; got != want {
   323  		t.Errorf("got [%v] want [%v]", got, want)
   324  	}
   325  }
   326  
   327  func TestIgnoreIllegalEscapeCharsInConstant(t *testing.T) {
   328  	src := `syntax = "proto2";
   329  	message Person {
   330  		optional string cpp_trigraph = 20 [default = "? \? ?? \?? \??? ??/ ?\?-"];
   331  	}`
   332  	p := newParserOn(src)
   333  	d, err := p.Parse()
   334  	if err != nil {
   335  		t.Fatal(err)
   336  	}
   337  	f := d.Elements[1].(*Message).Elements[0].(*NormalField)
   338  	if got, want := f.Options[0].Constant.Source, "? \\? ?? \\?? \\??? ??/ ?\\?-"; got != want {
   339  		t.Errorf("got [%v] want [%v]", got, want)
   340  	}
   341  }
   342  
   343  func TestFieldCustomOptionExtendedIdent(t *testing.T) {
   344  	proto := `Type field = 1 [(validate.rules).enum.defined_only = true];`
   345  	p := newParserOn(proto)
   346  	f := newNormalField()
   347  	err := f.parse(p)
   348  	if err != nil {
   349  		t.Fatal(err)
   350  	}
   351  	if got, want := f.Options[0].Name, "(validate.rules).enum.defined_only"; got != want {
   352  		t.Errorf("got [%v] want [%v]", got, want)
   353  	}
   354  }
   355  
   356  // issue #50
   357  func TestNestedAggregateConstants(t *testing.T) {
   358  	src := `syntax = "proto3";
   359  
   360  	package baz;
   361  
   362  	option (foo.bar) = {
   363  	  woot: 100
   364  	  foo {
   365  		hello: 200
   366  		hello2: 300
   367  		bar {
   368  			hello3: 400
   369  		}
   370  	  }
   371  	};`
   372  	p := newParserOn(src)
   373  	proto, err := p.Parse()
   374  	if err != nil {
   375  		t.Error(err)
   376  	}
   377  	option := proto.Elements[2].(*Option)
   378  	if got, want := option.Name, "(foo.bar)"; got != want {
   379  		t.Errorf("got [%v] want [%v]", got, want)
   380  	}
   381  	if got, want := len(option.Constant.Map), 2; got != want {
   382  		t.Errorf("got [%v] want [%v]", got, want)
   383  	}
   384  	m := option.Constant.Map
   385  	if got, want := m["woot"].Source, "100"; got != want {
   386  		t.Errorf("got [%v] want [%v]", got, want)
   387  	}
   388  	if got, want := len(m["foo"].Map), 3; got != want {
   389  		t.Errorf("got [%v] want [%v]", got, want)
   390  	}
   391  	m = m["foo"].Map
   392  	if got, want := len(m["bar"].Map), 1; got != want {
   393  		t.Errorf("got [%v] want [%v]", got, want)
   394  	}
   395  	if got, want := m["bar"].Map["hello3"].Source, "400"; got != want {
   396  		t.Errorf("got [%v] want [%v]", got, want)
   397  	}
   398  	if got, want := option.Constant.OrderedMap[1].Name, "foo"; got != want {
   399  		t.Errorf("got [%v] want [%v]", got, want)
   400  	}
   401  	if got, want := option.Constant.OrderedMap[1].OrderedMap[2].Name, "bar"; got != want {
   402  		t.Errorf("got [%v] want [%v]", got, want)
   403  	}
   404  	if got, want := option.Constant.OrderedMap[1].OrderedMap[2].OrderedMap[0].Source, "400"; got != want {
   405  		t.Errorf("got [%v] want [%v]", got, want)
   406  	}
   407  	if got, want := len(option.AggregatedConstants), 4; got != want {
   408  		t.Errorf("got [%v] want [%v]", got, want)
   409  	}
   410  	// for _, each := range option.AggregatedConstants {
   411  	// 	t.Logf("%#v=%v\n", each, each.SourceRepresentation())
   412  	// }
   413  }
   414  
   415  // Issue #59
   416  func TestMultiLineOptionAggregateValue(t *testing.T) {
   417  	src := `rpc ListTransferLogs(ListTransferLogsRequest)
   418  	returns (ListTransferLogsResponse) {
   419  		option (google.api.http) = {
   420  		get: "/v1/{parent=projects/*/locations/*/transferConfigs/*/runs/*}/"
   421  			"transferLogs"
   422  		};
   423  }`
   424  	p := newParserOn(src)
   425  	rpc := new(RPC)
   426  	p.next()
   427  	err := rpc.parse(p)
   428  	if err != nil {
   429  		t.Error(err)
   430  	}
   431  	get := rpc.Options[0].Constant.Map["get"]
   432  	if got, want := get.Source, "/v1/{parent=projects/*/locations/*/transferConfigs/*/runs/*}/transferLogs"; got != want {
   433  		t.Errorf("got [%v] want [%v]", got, want)
   434  	}
   435  }
   436  
   437  // issue #76
   438  func TestOptionAggregateCanUseKeyword(t *testing.T) {
   439  	src := `message User {
   440  		string email = 3 [(validate.field) = {required: true}];
   441  	}`
   442  	p := newParserOn(src)
   443  	_, err := p.Parse()
   444  	if err != nil {
   445  		t.Error(err)
   446  	}
   447  }
   448  
   449  // issue #77
   450  func TestOptionAggregateWithRepeatedValues(t *testing.T) {
   451  	src := `message Envelope {
   452  		int64 not_in = 15 [(validate.rules).int64 = {not_in: [40, 45]}];
   453  		int64 in = 16 [(validate.rules).int64 = {in: [[1],[2]]}];
   454  	}`
   455  	p := newParserOn(src)
   456  	def, err := p.Parse()
   457  	if err != nil {
   458  		t.Error(err)
   459  	}
   460  	field := def.Elements[0].(*Message).Elements[0].(*NormalField)
   461  	notIn := field.Options[0].Constant.Map["not_in"]
   462  	if got, want := len(notIn.Array), 2; got != want {
   463  		t.Errorf("got [%v] want [%v]", got, want)
   464  	}
   465  	if got, want := notIn.Array[0].Source, "40"; got != want {
   466  		t.Errorf("got [%v] want [%v]", got, want)
   467  	}
   468  	if got, want := notIn.Array[1].Source, "45"; got != want {
   469  		t.Errorf("got [%v] want [%v]", got, want)
   470  	}
   471  }
   472  
   473  func TestInvalidOptionAggregateWithRepeatedValues(t *testing.T) {
   474  	src := `message Bogus {
   475  		int64 a = 1 [a = {not_in: [40 syntax]}];
   476  	}`
   477  	p := newParserOn(src)
   478  	_, err := p.Parse()
   479  	if err == nil {
   480  		t.Error("expected syntax error")
   481  	}
   482  }
   483  
   484  // issue #79
   485  func TestUseOfSemicolonsInAggregatedConstants(t *testing.T) {
   486  	src := `rpc Test(Void) returns (Void) {
   487  				option (google.api.http) = {
   488  					post: "/api/v1/test";
   489  					body: "*"; // ignored comment
   490  				};
   491  			}`
   492  	p := newParserOn(src)
   493  	rpc := new(RPC)
   494  	p.next()
   495  	err := rpc.parse(p)
   496  	if err != nil {
   497  		t.Fatal(err)
   498  	}
   499  	if got, want := len(rpc.Elements), 1; got != want {
   500  		t.Errorf("got [%v] want [%v]", got, want)
   501  	}
   502  	opt := rpc.Elements[0].(*Option)
   503  	if got, want := len(opt.Constant.Map), 2; got != want {
   504  		t.Fatalf("got [%v] want [%v]", got, want)
   505  	}
   506  	// old access to map
   507  	if got, want := opt.Constant.Map["body"].Source, "*"; got != want {
   508  		t.Errorf("got [%v] want [%v]", got, want)
   509  	}
   510  	// new access to map
   511  	body, ok := opt.Constant.OrderedMap.Get("body")
   512  	if !ok {
   513  		t.Fatal("expected body key")
   514  	}
   515  	if got, want := body.Source, "*"; got != want {
   516  		t.Errorf("got [%v] want [%v]", got, want)
   517  	}
   518  	// for _, each := range opt.Constant.OrderedMap {
   519  	// 	t.Log(each)
   520  	// }
   521  }
   522  
   523  func TestParseNestedSelectorInAggregatedConstant(t *testing.T) {
   524  	src := `rpc Test(Void) returns (Void) {
   525  		option (google.api.http) = {
   526  			get: "/api/v1/test"
   527  			additional_bindings.post: "/api/v1/test"
   528  			additional_bindings.body: "*"
   529  		};
   530  	}`
   531  	p := newParserOn(src)
   532  	rpc := new(RPC)
   533  	p.next()
   534  	err := rpc.parse(p)
   535  	if err != nil {
   536  		t.Fatal(err)
   537  	}
   538  	if got, want := rpc.Options[0].Constant.Map["get"].Source, "/api/v1/test"; got != want {
   539  		t.Errorf("got [%v] want [%v]", got, want)
   540  	}
   541  	if got, want := rpc.Options[0].Constant.Map["additional_bindings.post"].Source, "/api/v1/test"; got != want {
   542  		t.Errorf("got [%v] want [%v]", got, want)
   543  	}
   544  	if got, want := rpc.Options[0].AggregatedConstants[2].Name, "additional_bindings.body"; got != want {
   545  		t.Errorf("got [%v] want [%v]", got, want)
   546  	}
   547  	if got, want := rpc.Options[0].AggregatedConstants[2].Source, "*"; got != want {
   548  		t.Errorf("got [%v] want [%v]", got, want)
   549  	}
   550  }
   551  
   552  func TestParseMultilineStringConstant(t *testing.T) {
   553  	src := `message Test {
   554  		string description = 3 [
   555  			(common.ui_field_desc) = "Description of the account"
   556  									 " domain (e.g. Team,"
   557  									 "Name User Account Directory)."
   558    		];
   559  	}`
   560  	p := newParserOn(src)
   561  	m := new(Message)
   562  	p.next()
   563  	err := m.parse(p)
   564  	if err != nil {
   565  		t.Fatal(err)
   566  	}
   567  	s := m.Elements[0].(*NormalField).Options[0].Constant.Source
   568  	if got, want := s, "Description of the account domain (e.g. Team,Name User Account Directory)."; got != want {
   569  		t.Errorf("got [%v] want [%v]", got, want)
   570  	}
   571  }
   572  
   573  func TestOptionWithRepeatedMessageValues(t *testing.T) {
   574  	src := `message Foo {
   575  		int64 a = 1 [b = {repeated_message_field: [{hello: 1}, {hello: 2}]}];
   576  	}`
   577  	p := newParserOn(src)
   578  	def, err := p.Parse()
   579  	if err != nil {
   580  		t.Errorf("expected no error but got %v", err)
   581  	}
   582  	opt := def.Elements[0].(*Message).Elements[0].(*NormalField).Options[0]
   583  	hello, ok := opt.AggregatedConstants[0].Array[0].OrderedMap.Get("hello")
   584  	if !ok {
   585  		t.Fail()
   586  	}
   587  	if got, want := hello.SourceRepresentation(), "1"; got != want {
   588  		t.Errorf("got [%v] want [%v]", got, want)
   589  	}
   590  }
   591  
   592  func TestOptionWithRepeatedMessageValuesWithArray(t *testing.T) {
   593  	src := `message Foo {
   594  		int64 a = 1 [ (bar.repeated_field_dep_option) =
   595  			{ hello: 1, repeated_dep: [
   596  				{ hello: 1, repeated_bar: [1, 2] },
   597  				{ hello: 3, repeated_bar: [3, 4] } ] } ];
   598  	}`
   599  	p := newParserOn(src)
   600  	def, err := p.Parse()
   601  	if err != nil {
   602  		t.Errorf("expected no error but got %v", err)
   603  	}
   604  	opt := def.Elements[0].(*Message).Elements[0].(*NormalField).Options[0]
   605  	hello, ok := opt.Constant.OrderedMap.Get("hello")
   606  	if !ok {
   607  		t.Fail()
   608  	}
   609  	if got, want := hello.SourceRepresentation(), "1"; got != want {
   610  		t.Errorf("got [%v] want [%v]", got, want)
   611  	}
   612  	repeatedDep, ok := opt.Constant.OrderedMap.Get("repeated_dep")
   613  	if !ok {
   614  		t.Fail()
   615  	}
   616  	if got, want := len(repeatedDep.Array), 2; got != want {
   617  		t.Errorf("got [%v] want [%v]", got, want)
   618  	}
   619  	hello, ok = repeatedDep.Array[0].OrderedMap.Get("hello")
   620  	if !ok {
   621  		t.Fail()
   622  	}
   623  	if got, want := hello.SourceRepresentation(), "1"; got != want {
   624  		t.Errorf("got [%v] want [%v]", got, want)
   625  	}
   626  	onetwo, ok := repeatedDep.Array[0].OrderedMap.Get("repeated_bar")
   627  	if !ok {
   628  		t.Fail()
   629  	}
   630  	if got, want := onetwo.Array[0].Source, "1"; got != want {
   631  		t.Errorf("got [%v] want [%v]", got, want)
   632  	}
   633  	if got, want := onetwo.Array[1].Source, "2"; got != want {
   634  		t.Errorf("got [%v] want [%v]", got, want)
   635  	}
   636  }
   637  
   638  // https://github.com/emicklei/proto/issues/99
   639  func TestFieldCustomOptionLeadingDot(t *testing.T) {
   640  	proto := `string app_entity_id = 4 [(.common.v1.some_custom_option) = { opt1: true opt2: false }];`
   641  	p := newParserOn(proto)
   642  	f := newNormalField()
   643  	err := f.parse(p)
   644  	if err != nil {
   645  		t.Fatal(err)
   646  	}
   647  	if got, want := f.Type, "string"; got != want {
   648  		t.Errorf("got [%v] want [%v]", got, want)
   649  	}
   650  	o := f.Options[0]
   651  	if got, want := o.Name, "(.common.v1.some_custom_option)"; got != want {
   652  		t.Fatalf("got [%v] want [%v]", got, want)
   653  	}
   654  }
   655  
   656  // https://github.com/emicklei/proto/issues/106
   657  func TestEmptyArrayInOptionStructure(t *testing.T) {
   658  	src := `
   659  	option (grpc.gateway.protoc_gen_swagger.options.openapiv2_schema) = {
   660  		json_schema : {
   661  		  title : "Frob a request"
   662  		  description : "blah blah blah"
   663  		  required : [ ]
   664  		  optional:["this"]
   665  		}
   666  	  };	
   667  	`
   668  	p := newParserOn(src)
   669  	p.next()
   670  	o := new(Option)
   671  	if err := o.parse(p); err != nil {
   672  		t.Fatal("testcase parse failed:", err)
   673  	}
   674  	s, ok := o.Constant.OrderedMap.Get("json_schema")
   675  	if !ok {
   676  		t.Fatal("expected json_schema literal")
   677  	}
   678  	// none
   679  	a, ok := s.OrderedMap.Get("required")
   680  	if !ok {
   681  		t.Fatal("expected required literal")
   682  	}
   683  	if len(a.Array) != 0 {
   684  		t.Fatal("expecting empty array")
   685  	}
   686  	// one
   687  	a, ok = s.OrderedMap.Get("optional")
   688  	if !ok {
   689  		t.Fatal("expected required literal")
   690  	}
   691  	if len(a.Array) != 1 {
   692  		t.Fatal("expecting one size array")
   693  	}
   694  	if got, want := a.Array[0].Source, "this"; got != want {
   695  		t.Fatalf("got [%s] want [%s]", got, want)
   696  	}
   697  }
   698  
   699  // https://github.com/emicklei/proto/issues/107
   700  func TestQuoteNotDroppedInOption(t *testing.T) {
   701  	src := `string name = 1 [ quote = '<="foo"' ];`
   702  	f := newNormalField()
   703  	if err := f.parse(newParserOn(src)); err != nil {
   704  		t.Fatal(err)
   705  	}
   706  	sr := f.Options[0].Constant.SourceRepresentation()
   707  	if got, want := sr, `'<="foo"'`; got != want {
   708  		t.Errorf("got [%s] want [%s]", got, want)
   709  	}
   710  }
   711  
   712  func TestWhatYouTypeIsWhatYouGetOptionValue(t *testing.T) {
   713  	src := `string n = 1 [ quote = 'm"\"/"' ];`
   714  	f := newNormalField()
   715  	if err := f.parse(newParserOn(src)); err != nil {
   716  		t.Fatal(err)
   717  	}
   718  	sr := f.Options[0].Constant.SourceRepresentation()
   719  	if got, want := sr, `'m"\"/"'`; got != want {
   720  		t.Errorf("got [%s] want [%s]", got, want)
   721  	}
   722  }
   723  
   724  func TestLiteralNoQuoteRuneSet(t *testing.T) {
   725  	l := Literal{
   726  		Source:   "foo",
   727  		IsString: true,
   728  	}
   729  	if got, want := l.SourceRepresentation(), "\"foo\""; got != want {
   730  		t.Errorf("got [%s] want [%s]", got, want)
   731  	}
   732  }
   733  
   734  func TestStringValuesParsedAsNumbers(t *testing.T) {
   735  	src := `VAL0 = 0 [(enum_opt) = '09'];`
   736  	f := new(EnumField)
   737  	if err := f.parse(newParserOn(src)); err != nil {
   738  		t.Fatal(err)
   739  	}
   740  	if got, want := f.ValueOption.Constant.Source, "09"; got != want {
   741  		t.Errorf("got [%v:%T] want [%v:%T]", got, got, want, want)
   742  	}
   743  }
   744  
   745  func TestCommentInsideArray(t *testing.T) {
   746  	src := `option test = {
   747  			scope_rules : [
   748  			  // A comment
   749  			  // Another comment
   750  			  {has : [ 
   751  				// comment on test
   752  				"test" 
   753  			  ]}
   754  			]
   755  		  };
   756  		`
   757  	opt, err := newParserOn(src).Parse()
   758  	if err != nil {
   759  		t.Fatal(err)
   760  	}
   761  	opt2 := opt.Elements[0].(*Option)
   762  	scp, _ := opt2.Constant.OrderedMap.Get("scope_rules")
   763  	elem0 := scp.Array[0]
   764  	t.Log("comment:", elem0.Comment.Lines)
   765  	has, _ := elem0.OrderedMap.Get("has")
   766  	if got, want := has.Array[0].Source, "test"; got != want {
   767  		t.Errorf("got [%v:%T] want [%v:%T]", got, got, want, want)
   768  	}
   769  	t.Log("comment:", has.Array[0].Comment.Lines)
   770  }
   771  

View as plain text