// Copyright (c) 2025 Ernest Micklei
//
// MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

package proto

import (
	"testing"
)

func TestOptionCases(t *testing.T) {
	for i, each := range []struct {
		proto     string
		name      string
		strLit    string
		nonStrLit string
	}{{
		`option (full).java_package = "com.example.foo";`,
		"(full).java_package",
		"com.example.foo",
		"",
	}, {
		`option Bool = true;`,
		"Bool",
		"",
		"true",
	}, {
		`option Float = -3.14E1;`,
		"Float",
		"",
		"-3.14E1",
	}, {
		`option (foo_options) = { opt1: 123 opt2: "baz" };`,
		"(foo_options)",
		"",
		"",
	}, {
		`option foo = []`,
		"foo",
		"",
		"",
	}, {
		`option optimize_for = SPEED;`,
		"optimize_for",
		"",
		"SPEED",
	}, {
		"option (my.enum.service.is.like).rpc = 1;",
		"(my.enum.service.is.like).rpc",
		"",
		"1",
	}, {
		`option (imported.oss.package).action = "literal-double-quotes";`,
		"(imported.oss.package).action",
		"literal-double-quotes",
		"",
	}, {
		`option (imported.oss.package).action = "key:\"literal-double-quotes-escaped\"";`,
		"(imported.oss.package).action",
		`key:\"literal-double-quotes-escaped\"`,
		"",
	}, {
		`option (imported.oss.package).action = 'literalsinglequotes';`,
		"(imported.oss.package).action",
		"literalsinglequotes",
		"",
	}, {
		`option (imported.oss.package).action = 'single-quotes.with/symbols';`,
		"(imported.oss.package).action",
		"single-quotes.with/symbols",
		"",
	}, {
		`option (imported.oss.package).action = 'single-quotes.with//double-slashes';`,
		"(imported.oss.package).action",
		"single-quotes.with//double-slashes",
		"",
	}, {
		`option features.(pb.go).api_level = API_OPAQUE;`,
		"features.(pb.go).api_level",
		"API_OPAQUE",
		"",
	}} {
		p := newParserOn(each.proto)
		pr, err := p.Parse()
		if err != nil {
			t.Fatal("testcase failed:", i, err)
		}
		if got, want := len(pr.Elements), 1; got != want {
			t.Fatalf("[%d] got [%v] want [%v]", i, got, want)
		}
		o := pr.Elements[0].(*Option)
		if got, want := o.Name, each.name; got != want {
			t.Errorf("[%d] got [%v] want [%v]", i, got, want)
		}
		if len(each.strLit) > 0 {
			if got, want := o.Constant.Source, each.strLit; got != want {
				t.Errorf("[%d] got [%v] want [%v]", i, got, want)
			}
		}
		if len(each.nonStrLit) > 0 {
			if got, want := o.Constant.Source, each.nonStrLit; got != want {
				t.Errorf("[%d] got [%v] want [%v]", i, got, want)
			}
		}
		if got, want := o.IsEmbedded, false; got != want {
			t.Errorf("[%d] got [%v] want [%v]", i, got, want)
		}
	}
}

func TestOptionComments(t *testing.T) {
	proto := `
// comment
option Help = "me"; // inline`
	p := newParserOn(proto)
	pr, err := p.Parse()
	if err != nil {
		t.Fatal(err)
	}
	o := pr.Elements[0].(*Option)
	if got, want := o.IsEmbedded, false; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := o.Comment != nil, true; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := o.Comment.Lines[0], " comment"; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := o.InlineComment != nil, true; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := o.InlineComment.Lines[0], " inline"; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := o.Position.Line, 3; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := o.Comment.Position.Line, 2; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := o.InlineComment.Position.Line, 3; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
}

func TestAggregateSyntax(t *testing.T) {
	proto := `
// usage:
message Bar {
  // alternative aggregate syntax (uses TextFormat):
  int32 b = 2 [(foo_options) = {
    opt1: 123,
    opt2: "baz"
  }];
}
	`
	p := newParserOn(proto)
	pr, err := p.Parse()
	if err != nil {
		t.Fatal(err)
	}
	o := pr.Elements[0].(*Message)
	f := o.Elements[0].(*NormalField)
	if got, want := len(f.Options), 1; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	ac := f.Options[0].Constant.Map
	if got, want := len(ac), 2; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := ac["opt1"].Source, "123"; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := o.Position.Line, 3; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := o.Comment.Position.String(), "<input>:2:1"; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := f.Position.String(), "<input>:5:3"; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := f.Options[0].Constant.Position.Line, 5; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	// check for AggregatedConstants
	list := f.Options[0].AggregatedConstants
	if got, want := list[0].Source, "123"; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := list[1].Source, "baz"; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
}

func TestNonPrimitiveOptionComment(t *testing.T) {
	proto := `
// comment
option Help = { string_field: "value" }; // inline`
	p := newParserOn(proto)
	pr, err := p.Parse()
	if err != nil {
		t.Fatal(err)
	}
	o := pr.Elements[0].(*Option)
	if got, want := o.Comment != nil, true; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := o.Comment.Lines[0], " comment"; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := o.InlineComment != nil, true; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := o.InlineComment.Lines[0], " inline"; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
}

func TestFieldCustomOptions(t *testing.T) {
	proto := `foo.bar lots = 1 [foo={hello:1}, bar=2];`
	p := newParserOn(proto)
	f := newNormalField()
	err := f.parse(p)
	if err != nil {
		t.Fatal(err)
	}
	if got, want := f.Type, "foo.bar"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := f.Name, "lots"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := len(f.Options), 2; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := f.Options[0].Name, "foo"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := f.Options[1].Name, "bar"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := f.Options[1].Constant.Source, "2"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	// check for AggregatedConstants
	if got, want := f.Options[0].AggregatedConstants[0].Name, "hello"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := f.Options[0].AggregatedConstants[0].PrintsColon, true; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := f.Options[0].AggregatedConstants[0].Source, "1"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
}

func TestIgnoreIllegalEscapeCharsInAggregatedConstants(t *testing.T) {
	src := `syntax = "proto3";
	message Person {
	  string name  = 3 [(validate.rules).string = {
						  pattern:   "^[^\d\s]+( [^\d\s]+)*$",
						  max_bytes: 256,
					   }];
	}`
	p := newParserOn(src)
	d, err := p.Parse()
	if err != nil {
		t.Fatal(err)
	}
	f := d.Elements[1].(*Message).Elements[0].(*NormalField)
	if got, want := f.Options[0].Name, "(validate.rules).string"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := len(f.Options[0].Constant.Map), 2; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := f.Options[0].Constant.Map["pattern"].Source, "^[^\\d\\s]+( [^\\d\\s]+)*$"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := len(f.Options[0].Constant.OrderedMap), 2; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := f.Options[0].Constant.OrderedMap[0].Name, "pattern"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := f.Options[0].Constant.OrderedMap[0].Source, "^[^\\d\\s]+( [^\\d\\s]+)*$"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	// check for AggregatedConstants
	if got, want := f.Options[0].AggregatedConstants[0].Name, "pattern"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := f.Options[0].AggregatedConstants[0].PrintsColon, true; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := f.Options[0].AggregatedConstants[0].Source, "^[^\\d\\s]+( [^\\d\\s]+)*$"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
}

func TestIgnoreIllegalEscapeCharsInConstant(t *testing.T) {
	src := `syntax = "proto2";
	message Person {
		optional string cpp_trigraph = 20 [default = "? \? ?? \?? \??? ??/ ?\?-"];
	}`
	p := newParserOn(src)
	d, err := p.Parse()
	if err != nil {
		t.Fatal(err)
	}
	f := d.Elements[1].(*Message).Elements[0].(*NormalField)
	if got, want := f.Options[0].Constant.Source, "? \\? ?? \\?? \\??? ??/ ?\\?-"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
}

func TestFieldCustomOptionExtendedIdent(t *testing.T) {
	proto := `Type field = 1 [(validate.rules).enum.defined_only = true];`
	p := newParserOn(proto)
	f := newNormalField()
	err := f.parse(p)
	if err != nil {
		t.Fatal(err)
	}
	if got, want := f.Options[0].Name, "(validate.rules).enum.defined_only"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
}

// issue #50
func TestNestedAggregateConstants(t *testing.T) {
	src := `syntax = "proto3";

	package baz;

	option (foo.bar) = {
	  woot: 100
	  foo {
		hello: 200
		hello2: 300
		bar {
			hello3: 400
		}
	  }
	};`
	p := newParserOn(src)
	proto, err := p.Parse()
	if err != nil {
		t.Error(err)
	}
	option := proto.Elements[2].(*Option)
	if got, want := option.Name, "(foo.bar)"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := len(option.Constant.Map), 2; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	m := option.Constant.Map
	if got, want := m["woot"].Source, "100"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := len(m["foo"].Map), 3; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	m = m["foo"].Map
	if got, want := len(m["bar"].Map), 1; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := m["bar"].Map["hello3"].Source, "400"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := option.Constant.OrderedMap[1].Name, "foo"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := option.Constant.OrderedMap[1].OrderedMap[2].Name, "bar"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := option.Constant.OrderedMap[1].OrderedMap[2].OrderedMap[0].Source, "400"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := len(option.AggregatedConstants), 4; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	// for _, each := range option.AggregatedConstants {
	// 	t.Logf("%#v=%v\n", each, each.SourceRepresentation())
	// }
}

// Issue #59
func TestMultiLineOptionAggregateValue(t *testing.T) {
	src := `rpc ListTransferLogs(ListTransferLogsRequest)
	returns (ListTransferLogsResponse) {
		option (google.api.http) = {
		get: "/v1/{parent=projects/*/locations/*/transferConfigs/*/runs/*}/"
			"transferLogs"
		};
}`
	p := newParserOn(src)
	rpc := new(RPC)
	p.next()
	err := rpc.parse(p)
	if err != nil {
		t.Error(err)
	}
	get := rpc.Options[0].Constant.Map["get"]
	if got, want := get.Source, "/v1/{parent=projects/*/locations/*/transferConfigs/*/runs/*}/transferLogs"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
}

// issue #76
func TestOptionAggregateCanUseKeyword(t *testing.T) {
	src := `message User {
		string email = 3 [(validate.field) = {required: true}];
	}`
	p := newParserOn(src)
	_, err := p.Parse()
	if err != nil {
		t.Error(err)
	}
}

// issue #77
func TestOptionAggregateWithRepeatedValues(t *testing.T) {
	src := `message Envelope {
		int64 not_in = 15 [(validate.rules).int64 = {not_in: [40, 45]}];
		int64 in = 16 [(validate.rules).int64 = {in: [[1],[2]]}];
	}`
	p := newParserOn(src)
	def, err := p.Parse()
	if err != nil {
		t.Error(err)
	}
	field := def.Elements[0].(*Message).Elements[0].(*NormalField)
	notIn := field.Options[0].Constant.Map["not_in"]
	if got, want := len(notIn.Array), 2; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := notIn.Array[0].Source, "40"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := notIn.Array[1].Source, "45"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
}

func TestInvalidOptionAggregateWithRepeatedValues(t *testing.T) {
	src := `message Bogus {
		int64 a = 1 [a = {not_in: [40 syntax]}];
	}`
	p := newParserOn(src)
	_, err := p.Parse()
	if err == nil {
		t.Error("expected syntax error")
	}
}

// issue #79
func TestUseOfSemicolonsInAggregatedConstants(t *testing.T) {
	src := `rpc Test(Void) returns (Void) {
				option (google.api.http) = {
					post: "/api/v1/test";
					body: "*"; // ignored comment
				};
			}`
	p := newParserOn(src)
	rpc := new(RPC)
	p.next()
	err := rpc.parse(p)
	if err != nil {
		t.Fatal(err)
	}
	if got, want := len(rpc.Elements), 1; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	opt := rpc.Elements[0].(*Option)
	if got, want := len(opt.Constant.Map), 2; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	// old access to map
	if got, want := opt.Constant.Map["body"].Source, "*"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	// new access to map
	body, ok := opt.Constant.OrderedMap.Get("body")
	if !ok {
		t.Fatal("expected body key")
	}
	if got, want := body.Source, "*"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	// for _, each := range opt.Constant.OrderedMap {
	// 	t.Log(each)
	// }
}

func TestParseNestedSelectorInAggregatedConstant(t *testing.T) {
	src := `rpc Test(Void) returns (Void) {
		option (google.api.http) = {
			get: "/api/v1/test"
			additional_bindings.post: "/api/v1/test"
			additional_bindings.body: "*"
		};
	}`
	p := newParserOn(src)
	rpc := new(RPC)
	p.next()
	err := rpc.parse(p)
	if err != nil {
		t.Fatal(err)
	}
	if got, want := rpc.Options[0].Constant.Map["get"].Source, "/api/v1/test"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := rpc.Options[0].Constant.Map["additional_bindings.post"].Source, "/api/v1/test"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := rpc.Options[0].AggregatedConstants[2].Name, "additional_bindings.body"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := rpc.Options[0].AggregatedConstants[2].Source, "*"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
}

func TestParseMultilineStringConstant(t *testing.T) {
	src := `message Test {
		string description = 3 [
			(common.ui_field_desc) = "Description of the account"
									 " domain (e.g. Team,"
									 "Name User Account Directory)."
  		];
	}`
	p := newParserOn(src)
	m := new(Message)
	p.next()
	err := m.parse(p)
	if err != nil {
		t.Fatal(err)
	}
	s := m.Elements[0].(*NormalField).Options[0].Constant.Source
	if got, want := s, "Description of the account domain (e.g. Team,Name User Account Directory)."; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
}

func TestOptionWithRepeatedMessageValues(t *testing.T) {
	src := `message Foo {
		int64 a = 1 [b = {repeated_message_field: [{hello: 1}, {hello: 2}]}];
	}`
	p := newParserOn(src)
	def, err := p.Parse()
	if err != nil {
		t.Errorf("expected no error but got %v", err)
	}
	opt := def.Elements[0].(*Message).Elements[0].(*NormalField).Options[0]
	hello, ok := opt.AggregatedConstants[0].Array[0].OrderedMap.Get("hello")
	if !ok {
		t.Fail()
	}
	if got, want := hello.SourceRepresentation(), "1"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
}

func TestOptionWithRepeatedMessageValuesWithArray(t *testing.T) {
	src := `message Foo {
		int64 a = 1 [ (bar.repeated_field_dep_option) =
			{ hello: 1, repeated_dep: [
				{ hello: 1, repeated_bar: [1, 2] },
				{ hello: 3, repeated_bar: [3, 4] } ] } ];
	}`
	p := newParserOn(src)
	def, err := p.Parse()
	if err != nil {
		t.Errorf("expected no error but got %v", err)
	}
	opt := def.Elements[0].(*Message).Elements[0].(*NormalField).Options[0]
	hello, ok := opt.Constant.OrderedMap.Get("hello")
	if !ok {
		t.Fail()
	}
	if got, want := hello.SourceRepresentation(), "1"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	repeatedDep, ok := opt.Constant.OrderedMap.Get("repeated_dep")
	if !ok {
		t.Fail()
	}
	if got, want := len(repeatedDep.Array), 2; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	hello, ok = repeatedDep.Array[0].OrderedMap.Get("hello")
	if !ok {
		t.Fail()
	}
	if got, want := hello.SourceRepresentation(), "1"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	onetwo, ok := repeatedDep.Array[0].OrderedMap.Get("repeated_bar")
	if !ok {
		t.Fail()
	}
	if got, want := onetwo.Array[0].Source, "1"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := onetwo.Array[1].Source, "2"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
}

// https://github.com/emicklei/proto/issues/99
func TestFieldCustomOptionLeadingDot(t *testing.T) {
	proto := `string app_entity_id = 4 [(.common.v1.some_custom_option) = { opt1: true opt2: false }];`
	p := newParserOn(proto)
	f := newNormalField()
	err := f.parse(p)
	if err != nil {
		t.Fatal(err)
	}
	if got, want := f.Type, "string"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	o := f.Options[0]
	if got, want := o.Name, "(.common.v1.some_custom_option)"; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
}

// https://github.com/emicklei/proto/issues/106
func TestEmptyArrayInOptionStructure(t *testing.T) {
	src := `
	option (grpc.gateway.protoc_gen_swagger.options.openapiv2_schema) = {
		json_schema : {
		  title : "Frob a request"
		  description : "blah blah blah"
		  required : [ ]
		  optional:["this"]
		}
	  };	
	`
	p := newParserOn(src)
	p.next()
	o := new(Option)
	if err := o.parse(p); err != nil {
		t.Fatal("testcase parse failed:", err)
	}
	s, ok := o.Constant.OrderedMap.Get("json_schema")
	if !ok {
		t.Fatal("expected json_schema literal")
	}
	// none
	a, ok := s.OrderedMap.Get("required")
	if !ok {
		t.Fatal("expected required literal")
	}
	if len(a.Array) != 0 {
		t.Fatal("expecting empty array")
	}
	// one
	a, ok = s.OrderedMap.Get("optional")
	if !ok {
		t.Fatal("expected required literal")
	}
	if len(a.Array) != 1 {
		t.Fatal("expecting one size array")
	}
	if got, want := a.Array[0].Source, "this"; got != want {
		t.Fatalf("got [%s] want [%s]", got, want)
	}
}

// https://github.com/emicklei/proto/issues/107
func TestQuoteNotDroppedInOption(t *testing.T) {
	src := `string name = 1 [ quote = '<="foo"' ];`
	f := newNormalField()
	if err := f.parse(newParserOn(src)); err != nil {
		t.Fatal(err)
	}
	sr := f.Options[0].Constant.SourceRepresentation()
	if got, want := sr, `'<="foo"'`; got != want {
		t.Errorf("got [%s] want [%s]", got, want)
	}
}

func TestWhatYouTypeIsWhatYouGetOptionValue(t *testing.T) {
	src := `string n = 1 [ quote = 'm"\"/"' ];`
	f := newNormalField()
	if err := f.parse(newParserOn(src)); err != nil {
		t.Fatal(err)
	}
	sr := f.Options[0].Constant.SourceRepresentation()
	if got, want := sr, `'m"\"/"'`; got != want {
		t.Errorf("got [%s] want [%s]", got, want)
	}
}

func TestLiteralNoQuoteRuneSet(t *testing.T) {
	l := Literal{
		Source:   "foo",
		IsString: true,
	}
	if got, want := l.SourceRepresentation(), "\"foo\""; got != want {
		t.Errorf("got [%s] want [%s]", got, want)
	}
}

func TestStringValuesParsedAsNumbers(t *testing.T) {
	src := `VAL0 = 0 [(enum_opt) = '09'];`
	f := new(EnumField)
	if err := f.parse(newParserOn(src)); err != nil {
		t.Fatal(err)
	}
	if got, want := f.ValueOption.Constant.Source, "09"; got != want {
		t.Errorf("got [%v:%T] want [%v:%T]", got, got, want, want)
	}
}

func TestCommentInsideArray(t *testing.T) {
	src := `option test = {
			scope_rules : [
			  // A comment
			  // Another comment
			  {has : [ 
				// comment on test
				"test" 
				// ignored comment at the end
			  ]}
			]
		  };
		`
	opt, err := newParserOn(src).Parse()
	if err != nil {
		t.Fatal(err)
	}
	opt2 := opt.Elements[0].(*Option)
	scp, _ := opt2.Constant.OrderedMap.Get("scope_rules")
	elem0 := scp.Array[0]
	t.Log("comment:", elem0.Comment.Lines)
	has, _ := elem0.OrderedMap.Get("has")
	if got, want := has.Array[0].Source, "test"; got != want {
		t.Errorf("got [%v:%T] want [%v:%T]", got, got, want, want)
	}
	t.Log("comment:", has.Array[0].Comment.Lines)
}

func TestParseBracedFullIdent(t *testing.T) {
	src := `features.(pb.go).api_level = API_OPAQUE;`
	p := newParserOn(src)
	o := new(Option)
	o.parseOptionName(p)
	if got, want := o.Name, "features.(pb.go).api_level"; got != want {
		t.Errorf("got [%[1]v:%[1]T] want [%[2]v:%[2]T]", got, want)
	}
}

func TestParseBracedFullIdentWithLeadingDot(t *testing.T) {
	src := `some.(.like).it.(.hot) = API_OPAQUE;`
	p := newParserOn(src)
	o := new(Option)
	o.parseOptionName(p)
	if got, want := o.Name, "some.(.like).it.(.hot)"; got != want {
		t.Errorf("got [%[1]v:%[1]T] want [%[2]v:%[2]T]", got, want)
	}
}
