|  | // Copyright 2018 The Go Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style | 
|  | // license that can be found in the LICENSE file. | 
|  |  | 
|  | package text | 
|  |  | 
|  | import ( | 
|  | "fmt" | 
|  | "math" | 
|  | "strings" | 
|  | "testing" | 
|  | "unicode/utf8" | 
|  |  | 
|  | "github.com/golang/protobuf/v2/internal/detrand" | 
|  | "github.com/golang/protobuf/v2/internal/flags" | 
|  | "github.com/golang/protobuf/v2/reflect/protoreflect" | 
|  | "github.com/google/go-cmp/cmp" | 
|  | "github.com/google/go-cmp/cmp/cmpopts" | 
|  | ) | 
|  |  | 
|  | // Disable detrand to enable direct comparisons on outputs. | 
|  | func init() { detrand.Disable() } | 
|  |  | 
|  | var S = fmt.Sprintf | 
|  | var V = ValueOf | 
|  | var ID = func(n protoreflect.Name) Value { return V(n) } | 
|  |  | 
|  | type Lst = []Value | 
|  | type Msg = [][2]Value | 
|  |  | 
|  | func Test(t *testing.T) { | 
|  | const space = " \n\r\t" | 
|  |  | 
|  | tests := []struct { | 
|  | in             string | 
|  | wantVal        Value | 
|  | wantOut        string | 
|  | wantOutBracket string | 
|  | wantOutASCII   string | 
|  | wantOutIndent  string | 
|  | wantErr        string | 
|  | }{{ | 
|  | in:            "", | 
|  | wantVal:       V(Msg{}), | 
|  | wantOutIndent: "\n", | 
|  | }, { | 
|  | in:      S("%s# hello%s", space, space), | 
|  | wantVal: V(Msg{}), | 
|  | }, { | 
|  | in:      S("%s# hello\rfoo:bar", space), | 
|  | wantVal: V(Msg{}), | 
|  | }, { | 
|  | // Comments only extend until the newline. | 
|  | in:            S("%s# hello\nfoo:bar", space), | 
|  | wantVal:       V(Msg{{ID("foo"), ID("bar")}}), | 
|  | wantOut:       "foo:bar", | 
|  | wantOutIndent: "foo: bar\n", | 
|  | }, { | 
|  | // NUL is an invalid whitespace since C++ uses C-strings. | 
|  | in:      "\x00", | 
|  | wantErr: `invalid "\x00" as identifier`, | 
|  | }, { | 
|  | in:      "foo:0", | 
|  | wantVal: V(Msg{{ID("foo"), V(uint32(0))}}), | 
|  | wantOut: "foo:0", | 
|  | }, { | 
|  | in:      S("%sfoo%s:0", space, space), | 
|  | wantVal: V(Msg{{ID("foo"), V(uint32(0))}}), | 
|  | }, { | 
|  | in:      "foo bar:0", | 
|  | wantErr: `expected ':' after message key`, | 
|  | }, { | 
|  | in:            "[foo]:0", | 
|  | wantVal:       V(Msg{{V("foo"), V(uint32(0))}}), | 
|  | wantOut:       "[foo]:0", | 
|  | wantOutIndent: "[foo]: 0\n", | 
|  | }, { | 
|  | in:      S("%s[%sfoo%s]%s:0", space, space, space, space), | 
|  | wantVal: V(Msg{{V("foo"), V(uint32(0))}}), | 
|  | }, { | 
|  | in:            "[proto.package.name]:0", | 
|  | wantVal:       V(Msg{{V("proto.package.name"), V(uint32(0))}}), | 
|  | wantOut:       "[proto.package.name]:0", | 
|  | wantOutIndent: "[proto.package.name]: 0\n", | 
|  | }, { | 
|  | in:      S("%s[%sproto.package.name%s]%s:0", space, space, space, space), | 
|  | wantVal: V(Msg{{V("proto.package.name"), V(uint32(0))}}), | 
|  | }, { | 
|  | in:            "['sub.domain.com\x2fpath\x2fto\x2fproto.package.name']:0", | 
|  | wantVal:       V(Msg{{V("sub.domain.com/path/to/proto.package.name"), V(uint32(0))}}), | 
|  | wantOut:       "[sub.domain.com/path/to/proto.package.name]:0", | 
|  | wantOutIndent: "[sub.domain.com/path/to/proto.package.name]: 0\n", | 
|  | }, { | 
|  | in:      "[\"sub.domain.com\x2fpath\x2fto\x2fproto.package.name\"]:0", | 
|  | wantVal: V(Msg{{V("sub.domain.com/path/to/proto.package.name"), V(uint32(0))}}), | 
|  | }, { | 
|  | in:      S("%s[%s'sub.domain.com\x2fpath\x2fto\x2fproto.package.name'%s]%s:0", space, space, space, space), | 
|  | wantVal: V(Msg{{V("sub.domain.com/path/to/proto.package.name"), V(uint32(0))}}), | 
|  | }, { | 
|  | in:      S("%s[%s\"sub.domain.com\x2fpath\x2fto\x2fproto.package.name\"%s]%s:0", space, space, space, space), | 
|  | wantVal: V(Msg{{V("sub.domain.com/path/to/proto.package.name"), V(uint32(0))}}), | 
|  | }, { | 
|  | in:            `['http://example.com/path/to/proto.package.name']:0`, | 
|  | wantVal:       V(Msg{{V("http://example.com/path/to/proto.package.name"), V(uint32(0))}}), | 
|  | wantOut:       `["http://example.com/path/to/proto.package.name"]:0`, | 
|  | wantOutIndent: `["http://example.com/path/to/proto.package.name"]: 0` + "\n", | 
|  | }, { | 
|  | in:      "[proto.package.name:0", | 
|  | wantErr: `invalid character ':', expected ']' at end of extension name`, | 
|  | }, { | 
|  | in:      "[proto.package name]:0", | 
|  | wantErr: `invalid character 'n', expected ']' at end of extension name`, | 
|  | }, { | 
|  | in:      `["proto.package" "name"]:0`, | 
|  | wantErr: `invalid character '"', expected ']' at end of extension name`, | 
|  | }, { | 
|  | in:      `["\z"]`, | 
|  | wantErr: `invalid escape code "\\z" in string`, | 
|  | }, { | 
|  | in:      "[$]", | 
|  | wantErr: `invalid "$" as identifier`, | 
|  | }, { | 
|  | // This parses fine, but should result in a error later since no | 
|  | // type name in proto will ever be just a number. | 
|  | in:      "[20]:0", | 
|  | wantVal: V(Msg{{V("20"), V(uint32(0))}}), | 
|  | wantOut: "[20]:0", | 
|  | }, { | 
|  | in:      "20:0", | 
|  | wantVal: V(Msg{{V(uint32(20)), V(uint32(0))}}), | 
|  | wantOut: "20:0", | 
|  | }, { | 
|  | in:      "0x20:0", | 
|  | wantVal: V(Msg{{V(uint32(0x20)), V(uint32(0))}}), | 
|  | wantOut: "32:0", | 
|  | }, { | 
|  | in:      "020:0", | 
|  | wantVal: V(Msg{{V(uint32(020)), V(uint32(0))}}), | 
|  | wantOut: "16:0", | 
|  | }, { | 
|  | in:      "-20:0", | 
|  | wantErr: `invalid "-20" as identifier`, | 
|  | }, { | 
|  | in: `foo:true bar:"s" baz:{} qux:[] wib:id`, | 
|  | wantVal: V(Msg{ | 
|  | {ID("foo"), V(true)}, | 
|  | {ID("bar"), V("s")}, | 
|  | {ID("baz"), V(Msg{})}, | 
|  | {ID("qux"), V(Lst{})}, | 
|  | {ID("wib"), ID("id")}, | 
|  | }), | 
|  | wantOut:       `foo:true bar:"s" baz:{} qux:[] wib:id`, | 
|  | wantOutIndent: "foo: true\nbar: \"s\"\nbaz: {}\nqux: []\nwib: id\n", | 
|  | }, { | 
|  | in: S(`%sfoo%s:%strue%s %sbar%s:%s"s"%s %sbaz%s:%s<>%s %squx%s:%s[]%s %swib%s:%sid%s`, | 
|  | space, space, space, space, space, space, space, space, space, space, space, space, space, space, space, space, space, space, space, space), | 
|  | wantVal: V(Msg{ | 
|  | {ID("foo"), V(true)}, | 
|  | {ID("bar"), V("s")}, | 
|  | {ID("baz"), V(Msg{})}, | 
|  | {ID("qux"), V(Lst{})}, | 
|  | {ID("wib"), ID("id")}, | 
|  | }), | 
|  | }, { | 
|  | in:            `foo:true;`, | 
|  | wantVal:       V(Msg{{ID("foo"), V(true)}}), | 
|  | wantOut:       "foo:true", | 
|  | wantOutIndent: "foo: true\n", | 
|  | }, { | 
|  | in:      `foo:true,`, | 
|  | wantVal: V(Msg{{ID("foo"), V(true)}}), | 
|  | }, { | 
|  | in:      `foo:bar;,`, | 
|  | wantErr: `invalid "," as identifier`, | 
|  | }, { | 
|  | in:      `foo:bar,;`, | 
|  | wantErr: `invalid ";" as identifier`, | 
|  | }, { | 
|  | in:      `footrue`, | 
|  | wantErr: `unexpected EOF`, | 
|  | }, { | 
|  | in:      `foo true`, | 
|  | wantErr: `expected ':' after message key`, | 
|  | }, { | 
|  | in:      `foo"s"`, | 
|  | wantErr: `expected ':' after message key`, | 
|  | }, { | 
|  | in:      `foo "s"`, | 
|  | wantErr: `expected ':' after message key`, | 
|  | }, { | 
|  | in:             `foo{}`, | 
|  | wantVal:        V(Msg{{ID("foo"), V(Msg{})}}), | 
|  | wantOut:        "foo:{}", | 
|  | wantOutBracket: "foo:<>", | 
|  | wantOutIndent:  "foo: {}\n", | 
|  | }, { | 
|  | in:      `foo {}`, | 
|  | wantVal: V(Msg{{ID("foo"), V(Msg{})}}), | 
|  | }, { | 
|  | in:      `foo<>`, | 
|  | wantVal: V(Msg{{ID("foo"), V(Msg{})}}), | 
|  | }, { | 
|  | in:      `foo <>`, | 
|  | wantVal: V(Msg{{ID("foo"), V(Msg{})}}), | 
|  | }, { | 
|  | in:      `foo[]`, | 
|  | wantErr: `expected ':' after message key`, | 
|  | }, { | 
|  | in:      `foo []`, | 
|  | wantErr: `expected ':' after message key`, | 
|  | }, { | 
|  | in:      `foo:truebar:true`, | 
|  | wantErr: `invalid ":" as identifier`, | 
|  | }, { | 
|  | in:            `foo:"s"bar:true`, | 
|  | wantVal:       V(Msg{{ID("foo"), V("s")}, {ID("bar"), V(true)}}), | 
|  | wantOut:       `foo:"s" bar:true`, | 
|  | wantOutIndent: "foo: \"s\"\nbar: true\n", | 
|  | }, { | 
|  | in:      `foo:0bar:true`, | 
|  | wantErr: `invalid "0bar" as number or bool`, | 
|  | }, { | 
|  | in:             `foo:{}bar:true`, | 
|  | wantVal:        V(Msg{{ID("foo"), V(Msg{})}, {ID("bar"), V(true)}}), | 
|  | wantOut:        "foo:{} bar:true", | 
|  | wantOutBracket: "foo:<> bar:true", | 
|  | wantOutIndent:  "foo: {}\nbar: true\n", | 
|  | }, { | 
|  | in:      `foo:[]bar:true`, | 
|  | wantVal: V(Msg{{ID("foo"), V(Lst{})}, {ID("bar"), V(true)}}), | 
|  | }, { | 
|  | in:             `foo{bar:true}`, | 
|  | wantVal:        V(Msg{{ID("foo"), V(Msg{{ID("bar"), V(true)}})}}), | 
|  | wantOut:        "foo:{bar:true}", | 
|  | wantOutBracket: "foo:<bar:true>", | 
|  | wantOutIndent:  "foo: {\n\tbar: true\n}\n", | 
|  | }, { | 
|  | in:      `foo<bar:true>`, | 
|  | wantVal: V(Msg{{ID("foo"), V(Msg{{ID("bar"), V(true)}})}}), | 
|  | }, { | 
|  | in:      `foo{bar:true,}`, | 
|  | wantVal: V(Msg{{ID("foo"), V(Msg{{ID("bar"), V(true)}})}}), | 
|  | }, { | 
|  | in:      `foo{bar:true;}`, | 
|  | wantVal: V(Msg{{ID("foo"), V(Msg{{ID("bar"), V(true)}})}}), | 
|  | }, { | 
|  | in:      `foo{`, | 
|  | wantErr: `unexpected EOF`, | 
|  | }, { | 
|  | in:      `foo{ `, | 
|  | wantErr: `unexpected EOF`, | 
|  | }, { | 
|  | in:      `foo{[`, | 
|  | wantErr: `unexpected EOF`, | 
|  | }, { | 
|  | in:      `foo{[ `, | 
|  | wantErr: `unexpected EOF`, | 
|  | }, { | 
|  | in:      `foo{bar:true,;}`, | 
|  | wantErr: `invalid ";" as identifier`, | 
|  | }, { | 
|  | in:      `foo{bar:true;,}`, | 
|  | wantErr: `invalid "," as identifier`, | 
|  | }, { | 
|  | in:             `foo<bar:{}>`, | 
|  | wantVal:        V(Msg{{ID("foo"), V(Msg{{ID("bar"), V(Msg{})}})}}), | 
|  | wantOut:        "foo:{bar:{}}", | 
|  | wantOutBracket: "foo:<bar:<>>", | 
|  | wantOutIndent:  "foo: {\n\tbar: {}\n}\n", | 
|  | }, { | 
|  | in:      `foo<bar:{>`, | 
|  | wantErr: `invalid character '>', expected '}' at end of message`, | 
|  | }, { | 
|  | in:      `foo<bar:{}`, | 
|  | wantErr: `unexpected EOF`, | 
|  | }, { | 
|  | in:             `arr:[]`, | 
|  | wantVal:        V(Msg{{ID("arr"), V(Lst{})}}), | 
|  | wantOut:        "arr:[]", | 
|  | wantOutBracket: "arr:[]", | 
|  | wantOutIndent:  "arr: []\n", | 
|  | }, { | 
|  | in:      `arr:[,]`, | 
|  | wantErr: `invalid "," as number or bool`, | 
|  | }, { | 
|  | in:      `arr:[0 0]`, | 
|  | wantErr: `invalid character '0', expected ']' at end of list`, | 
|  | }, { | 
|  | in:             `arr:["foo" "bar"]`, | 
|  | wantVal:        V(Msg{{ID("arr"), V(Lst{V("foobar")})}}), | 
|  | wantOut:        `arr:["foobar"]`, | 
|  | wantOutBracket: `arr:["foobar"]`, | 
|  | wantOutIndent:  "arr: [\n\t\"foobar\"\n]\n", | 
|  | }, { | 
|  | in:      `arr:[0,]`, | 
|  | wantErr: `invalid "]" as number or bool`, | 
|  | }, { | 
|  | in: `arr:[true,0,"",id,[],{}]`, | 
|  | wantVal: V(Msg{{ID("arr"), V(Lst{ | 
|  | V(true), V(uint32(0)), V(""), ID("id"), V(Lst{}), V(Msg{}), | 
|  | })}}), | 
|  | wantOut:        `arr:[true,0,"",id,[],{}]`, | 
|  | wantOutBracket: `arr:[true,0,"",id,[],<>]`, | 
|  | wantOutIndent:  "arr: [\n\ttrue,\n\t0,\n\t\"\",\n\tid,\n\t[],\n\t{}\n]\n", | 
|  | }, { | 
|  | in: S(`arr:[%strue%s,%s0%s,%s""%s,%sid%s,%s[]%s,%s{}%s]`, | 
|  | space, space, space, space, space, space, space, space, space, space, space, space), | 
|  | wantVal: V(Msg{{ID("arr"), V(Lst{ | 
|  | V(true), V(uint32(0)), V(""), ID("id"), V(Lst{}), V(Msg{}), | 
|  | })}}), | 
|  | }, { | 
|  | in:      `arr:[`, | 
|  | wantErr: `unexpected EOF`, | 
|  | }, { | 
|  | in:      `{`, | 
|  | wantErr: `invalid "{" as identifier`, | 
|  | }, { | 
|  | in:      `<`, | 
|  | wantErr: `invalid "<" as identifier`, | 
|  | }, { | 
|  | in:      `[`, | 
|  | wantErr: "unexpected EOF", | 
|  | }, { | 
|  | in:      `}`, | 
|  | wantErr: "1 bytes of unconsumed input", | 
|  | }, { | 
|  | in:      `>`, | 
|  | wantErr: "1 bytes of unconsumed input", | 
|  | }, { | 
|  | in:      `]`, | 
|  | wantErr: `invalid "]" as identifier`, | 
|  | }, { | 
|  | in:      `str: "'"`, | 
|  | wantVal: V(Msg{{ID("str"), V(`'`)}}), | 
|  | wantOut: `str:"'"`, | 
|  | }, { | 
|  | in:      `str: '"'`, | 
|  | wantVal: V(Msg{{ID("str"), V(`"`)}}), | 
|  | wantOut: `str:"\""`, | 
|  | }, { | 
|  | // String that has as few escaped characters as possible. | 
|  | in: `str: ` + func() string { | 
|  | var b []byte | 
|  | for i := 0; i < utf8.RuneSelf; i++ { | 
|  | switch i { | 
|  | case 0, '\\', '\n', '\'': // these must be escaped, so ignore them | 
|  | default: | 
|  | b = append(b, byte(i)) | 
|  | } | 
|  | } | 
|  | return "'" + string(b) + "'" | 
|  | }(), | 
|  | wantVal:      V(Msg{{ID("str"), V("\x01\x02\x03\x04\x05\x06\a\b\t\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~\u007f")}}), | 
|  | wantOut:      `str:"\x01\x02\x03\x04\x05\x06\x07\x08\t\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_` + "`abcdefghijklmnopqrstuvwxyz{|}~\x7f\"", | 
|  | wantOutASCII: `str:"\x01\x02\x03\x04\x05\x06\x07\x08\t\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_` + "`abcdefghijklmnopqrstuvwxyz{|}~\x7f\"", | 
|  | }, { | 
|  | in:           "str: '\xde\xad\xbe\xef'", | 
|  | wantVal:      V(Msg{{ID("str"), V("\xde\xad\xbe\xef")}}), | 
|  | wantOut:      "str:\"\u07ad\\xbe\\xef\"", | 
|  | wantOutASCII: `str:"\u07ad\xbe\xef"`, | 
|  | wantErr:      "invalid UTF-8 detected", | 
|  | }, { | 
|  | // Valid UTF-8 wire encoding, but sub-optimal encoding. | 
|  | in:           "str: '\xc0\x80'", | 
|  | wantVal:      V(Msg{{ID("str"), V("\xc0\x80")}}), | 
|  | wantOut:      `str:"\xc0\x80"`, | 
|  | wantOutASCII: `str:"\xc0\x80"`, | 
|  | wantErr:      "invalid UTF-8 detected", | 
|  | }, { | 
|  | // Valid UTF-8 wire encoding, but invalid rune (surrogate pair). | 
|  | in:           "str: '\xed\xa0\x80'", | 
|  | wantVal:      V(Msg{{ID("str"), V("\xed\xa0\x80")}}), | 
|  | wantOut:      `str:"\xed\xa0\x80"`, | 
|  | wantOutASCII: `str:"\xed\xa0\x80"`, | 
|  | wantErr:      "invalid UTF-8 detected", | 
|  | }, { | 
|  | // Valid UTF-8 wire encoding, but invalid rune (above max rune). | 
|  | in:           "str: '\xf7\xbf\xbf\xbf'", | 
|  | wantVal:      V(Msg{{ID("str"), V("\xf7\xbf\xbf\xbf")}}), | 
|  | wantOut:      `str:"\xf7\xbf\xbf\xbf"`, | 
|  | wantOutASCII: `str:"\xf7\xbf\xbf\xbf"`, | 
|  | wantErr:      "invalid UTF-8 detected", | 
|  | }, { | 
|  | // Valid UTF-8 wire encoding of the RuneError rune. | 
|  | in:           "str: '\xef\xbf\xbd'", | 
|  | wantVal:      V(Msg{{ID("str"), V(string(utf8.RuneError))}}), | 
|  | wantOut:      `str:"` + string(utf8.RuneError) + `"`, | 
|  | wantOutASCII: `str:"\ufffd"`, | 
|  | }, { | 
|  | in:           "str: 'hello\u1234world'", | 
|  | wantVal:      V(Msg{{ID("str"), V("hello\u1234world")}}), | 
|  | wantOut:      "str:\"hello\u1234world\"", | 
|  | wantOutASCII: `str:"hello\u1234world"`, | 
|  | }, { | 
|  | in:           `str: '\"\'\\\?\a\b\n\r\t\v\f\1\12\123\xA\xaB\x12\uAb8f\U0010FFFF'`, | 
|  | wantVal:      V(Msg{{ID("str"), V("\"'\\?\a\b\n\r\t\v\f\x01\nS\n\xab\x12\uab8f\U0010ffff")}}), | 
|  | wantOut:      `str:"\"'\\?\x07\x08\n\r\t\x0b\x0c\x01\nS\n\xab\x12` + "\uab8f\U0010ffff" + `"`, | 
|  | wantOutASCII: `str:"\"'\\?\x07\x08\n\r\t\x0b\x0c\x01\nS\n\xab\x12\uab8f\U0010ffff"`, | 
|  | }, { | 
|  | in:      `str: '`, | 
|  | wantErr: `unexpected EOF`, | 
|  | }, { | 
|  | in:      `str: '\`, | 
|  | wantErr: `unexpected EOF`, | 
|  | }, { | 
|  | in:      `str: '\'`, | 
|  | wantErr: `unexpected EOF`, | 
|  | }, { | 
|  | in:      `str: '\8'`, | 
|  | wantErr: `invalid escape code "\\8" in string`, | 
|  | }, { | 
|  | in:           `str: '\1x'`, | 
|  | wantVal:      V(Msg{{ID("str"), V("\001x")}}), | 
|  | wantOut:      `str:"\x01x"`, | 
|  | wantOutASCII: `str:"\x01x"`, | 
|  | }, { | 
|  | in:           `str: '\12x'`, | 
|  | wantVal:      V(Msg{{ID("str"), V("\012x")}}), | 
|  | wantOut:      `str:"\nx"`, | 
|  | wantOutASCII: `str:"\nx"`, | 
|  | }, { | 
|  | in:           `str: '\123x'`, | 
|  | wantVal:      V(Msg{{ID("str"), V("\123x")}}), | 
|  | wantOut:      `str:"Sx"`, | 
|  | wantOutASCII: `str:"Sx"`, | 
|  | }, { | 
|  | in:           `str: '\1234x'`, | 
|  | wantVal:      V(Msg{{ID("str"), V("\1234x")}}), | 
|  | wantOut:      `str:"S4x"`, | 
|  | wantOutASCII: `str:"S4x"`, | 
|  | }, { | 
|  | in:           `str: '\1'`, | 
|  | wantVal:      V(Msg{{ID("str"), V("\001")}}), | 
|  | wantOut:      `str:"\x01"`, | 
|  | wantOutASCII: `str:"\x01"`, | 
|  | }, { | 
|  | in:           `str: '\12'`, | 
|  | wantVal:      V(Msg{{ID("str"), V("\012")}}), | 
|  | wantOut:      `str:"\n"`, | 
|  | wantOutASCII: `str:"\n"`, | 
|  | }, { | 
|  | in:           `str: '\123'`, | 
|  | wantVal:      V(Msg{{ID("str"), V("\123")}}), | 
|  | wantOut:      `str:"S"`, | 
|  | wantOutASCII: `str:"S"`, | 
|  | }, { | 
|  | in:           `str: '\1234'`, | 
|  | wantVal:      V(Msg{{ID("str"), V("\1234")}}), | 
|  | wantOut:      `str:"S4"`, | 
|  | wantOutASCII: `str:"S4"`, | 
|  | }, { | 
|  | in:           `str: '\377'`, | 
|  | wantVal:      V(Msg{{ID("str"), V("\377")}}), | 
|  | wantOut:      `str:"\xff"`, | 
|  | wantOutASCII: `str:"\xff"`, | 
|  | }, { | 
|  | // Overflow octal escape. | 
|  | in:      `str: '\400'`, | 
|  | wantErr: `invalid octal escape code "\\400" in string`, | 
|  | }, { | 
|  | in:           `str: '\xfx'`, | 
|  | wantVal:      V(Msg{{ID("str"), V("\x0fx")}}), | 
|  | wantOut:      `str:"\x0fx"`, | 
|  | wantOutASCII: `str:"\x0fx"`, | 
|  | }, { | 
|  | in:           `str: '\xffx'`, | 
|  | wantVal:      V(Msg{{ID("str"), V("\xffx")}}), | 
|  | wantOut:      `str:"\xffx"`, | 
|  | wantOutASCII: `str:"\xffx"`, | 
|  | }, { | 
|  | in:           `str: '\xfffx'`, | 
|  | wantVal:      V(Msg{{ID("str"), V("\xfffx")}}), | 
|  | wantOut:      `str:"\xfffx"`, | 
|  | wantOutASCII: `str:"\xfffx"`, | 
|  | }, { | 
|  | in:           `str: '\xf'`, | 
|  | wantVal:      V(Msg{{ID("str"), V("\x0f")}}), | 
|  | wantOut:      `str:"\x0f"`, | 
|  | wantOutASCII: `str:"\x0f"`, | 
|  | }, { | 
|  | in:           `str: '\xff'`, | 
|  | wantVal:      V(Msg{{ID("str"), V("\xff")}}), | 
|  | wantOut:      `str:"\xff"`, | 
|  | wantOutASCII: `str:"\xff"`, | 
|  | }, { | 
|  | in:           `str: '\xfff'`, | 
|  | wantVal:      V(Msg{{ID("str"), V("\xfff")}}), | 
|  | wantOut:      `str:"\xfff"`, | 
|  | wantOutASCII: `str:"\xfff"`, | 
|  | }, { | 
|  | in:      `str: '\xz'`, | 
|  | wantErr: `invalid hex escape code "\\x" in string`, | 
|  | }, { | 
|  | in:      `str: '\uPo'`, | 
|  | wantErr: `unexpected EOF`, | 
|  | }, { | 
|  | in:      `str: '\uPoo'`, | 
|  | wantErr: `invalid Unicode escape code "\\uPoo'" in string`, | 
|  | }, { | 
|  | in:      `str: '\uPoop'`, | 
|  | wantErr: `invalid Unicode escape code "\\uPoop" in string`, | 
|  | }, { | 
|  | // Unmatched surrogate pair. | 
|  | in:      `str: '\uDEAD'`, | 
|  | wantErr: `unexpected EOF`, // trying to reader other half | 
|  | }, { | 
|  | // Surrogate pair with invalid other half. | 
|  | in:      `str: '\uDEAD\u0000'`, | 
|  | wantErr: `invalid Unicode escape code "\\u0000" in string`, | 
|  | }, { | 
|  | // Properly matched surrogate pair. | 
|  | in:           `str: '\uD800\uDEAD'`, | 
|  | wantVal:      V(Msg{{ID("str"), V("𐊭")}}), | 
|  | wantOut:      `str:"𐊭"`, | 
|  | wantOutASCII: `str:"\U000102ad"`, | 
|  | }, { | 
|  | // Overflow on Unicode rune. | 
|  | in:      `str: '\U00110000'`, | 
|  | wantErr: `invalid Unicode escape code "\\U00110000" in string`, | 
|  | }, { | 
|  | in:      `str: '\z'`, | 
|  | wantErr: `invalid escape code "\\z" in string`, | 
|  | }, { | 
|  | // Strings cannot have NUL literal since C-style strings forbid them. | 
|  | in:      "str: '\x00'", | 
|  | wantErr: `invalid character '\x00' in string`, | 
|  | }, { | 
|  | // Strings cannot have newline literal. The C++ permits them if an | 
|  | // option is specified to allow them. In Go, we always forbid them. | 
|  | in:      "str: '\n'", | 
|  | wantErr: `invalid character '\n' in string`, | 
|  | }, { | 
|  | in:           "name: \"My name is \"\n\"elsewhere\"", | 
|  | wantVal:      V(Msg{{ID("name"), V("My name is elsewhere")}}), | 
|  | wantOut:      `name:"My name is elsewhere"`, | 
|  | wantOutASCII: `name:"My name is elsewhere"`, | 
|  | }, { | 
|  | in:      "name: 'My name is '\n'elsewhere'", | 
|  | wantVal: V(Msg{{ID("name"), V("My name is elsewhere")}}), | 
|  | }, { | 
|  | in:      "name: 'My name is '\n\"elsewhere\"", | 
|  | wantVal: V(Msg{{ID("name"), V("My name is elsewhere")}}), | 
|  | }, { | 
|  | in:      "name: \"My name is \"\n'elsewhere'", | 
|  | wantVal: V(Msg{{ID("name"), V("My name is elsewhere")}}), | 
|  | }, { | 
|  | in:      "name: \"My \"'name '\"is \"\n'elsewhere'", | 
|  | wantVal: V(Msg{{ID("name"), V("My name is elsewhere")}}), | 
|  | }, { | 
|  | in:      `crazy:"x'"'\""\''"'z"`, | 
|  | wantVal: V(Msg{{ID("crazy"), V(`x'""''z`)}}), | 
|  | }, { | 
|  | in: `nums: [t,T,true,True,TRUE,f,F,false,False,FALSE]`, | 
|  | wantVal: V(Msg{{ID("nums"), V(Lst{ | 
|  | V(true), | 
|  | ID("T"), | 
|  | V(true), | 
|  | V(true), | 
|  | ID("TRUE"), | 
|  | V(false), | 
|  | ID("F"), | 
|  | V(false), | 
|  | V(false), | 
|  | ID("FALSE"), | 
|  | })}}), | 
|  | wantOut:       "nums:[true,T,true,true,TRUE,false,F,false,false,FALSE]", | 
|  | wantOutIndent: "nums: [\n\ttrue,\n\tT,\n\ttrue,\n\ttrue,\n\tTRUE,\n\tfalse,\n\tF,\n\tfalse,\n\tfalse,\n\tFALSE\n]\n", | 
|  | }, { | 
|  | in: `nums: [nan,inf,-inf,NaN,NAN,Inf,INF]`, | 
|  | wantVal: V(Msg{{ID("nums"), V(Lst{ | 
|  | V(math.NaN()), | 
|  | V(math.Inf(+1)), | 
|  | V(math.Inf(-1)), | 
|  | ID("NaN"), | 
|  | ID("NAN"), | 
|  | ID("Inf"), | 
|  | ID("INF"), | 
|  | })}}), | 
|  | wantOut:       "nums:[nan,inf,-inf,NaN,NAN,Inf,INF]", | 
|  | wantOutIndent: "nums: [\n\tnan,\n\tinf,\n\t-inf,\n\tNaN,\n\tNAN,\n\tInf,\n\tINF\n]\n", | 
|  | }, { | 
|  | // C++ permits this, but we currently reject this. | 
|  | in:      `num: -nan`, | 
|  | wantErr: `invalid "-nan" as number or bool`, | 
|  | }, { | 
|  | in: `nums: [0,-0,-9876543210,9876543210,0x0,0x0123456789abcdef,-0x0123456789abcdef,01234567,-01234567]`, | 
|  | wantVal: V(Msg{{ID("nums"), V(Lst{ | 
|  | V(uint32(0)), | 
|  | V(int32(-0)), | 
|  | V(int64(-9876543210)), | 
|  | V(uint64(9876543210)), | 
|  | V(uint32(0x0)), | 
|  | V(uint64(0x0123456789abcdef)), | 
|  | V(int64(-0x0123456789abcdef)), | 
|  | V(uint64(01234567)), | 
|  | V(int64(-01234567)), | 
|  | })}}), | 
|  | wantOut:       "nums:[0,0,-9876543210,9876543210,0,81985529216486895,-81985529216486895,342391,-342391]", | 
|  | wantOutIndent: "nums: [\n\t0,\n\t0,\n\t-9876543210,\n\t9876543210,\n\t0,\n\t81985529216486895,\n\t-81985529216486895,\n\t342391,\n\t-342391\n]\n", | 
|  | }, { | 
|  | in: `nums: [0.,0f,1f,10f,-0f,-1f,-10f,1.0,0.1e-3,1.5e+5,1e10,.0]`, | 
|  | wantVal: V(Msg{{ID("nums"), V(Lst{ | 
|  | V(0.0), | 
|  | V(0.0), | 
|  | V(1.0), | 
|  | V(10.0), | 
|  | V(-0.0), | 
|  | V(-1.0), | 
|  | V(-10.0), | 
|  | V(1.0), | 
|  | V(0.1e-3), | 
|  | V(1.5e+5), | 
|  | V(1.0e+10), | 
|  | V(0.0), | 
|  | })}}), | 
|  | wantOut:       "nums:[0,0,1,10,0,-1,-10,1,0.0001,150000,1e+10,0]", | 
|  | wantOutIndent: "nums: [\n\t0,\n\t0,\n\t1,\n\t10,\n\t0,\n\t-1,\n\t-10,\n\t1,\n\t0.0001,\n\t150000,\n\t1e+10,\n\t0\n]\n", | 
|  | }, { | 
|  | in: `nums: [0xbeefbeef,0xbeefbeefbeefbeef]`, | 
|  | wantVal: V(Msg{{ID("nums"), func() Value { | 
|  | if flags.Proto1Legacy { | 
|  | return V(Lst{V(int32(-1091584273)), V(int64(-4688318750159552785))}) | 
|  | } else { | 
|  | return V(Lst{V(uint32(0xbeefbeef)), V(uint64(0xbeefbeefbeefbeef))}) | 
|  | } | 
|  | }()}}), | 
|  | }, { | 
|  | in:      `num: +0`, | 
|  | wantErr: `invalid "+0" as number or bool`, | 
|  | }, { | 
|  | in:      `num: 01.1234`, | 
|  | wantErr: `invalid "01.1234" as number or bool`, | 
|  | }, { | 
|  | in:      `num: 0x`, | 
|  | wantErr: `invalid "0x" as number or bool`, | 
|  | }, { | 
|  | in:      `num: 0xX`, | 
|  | wantErr: `invalid "0xX" as number or bool`, | 
|  | }, { | 
|  | in:      `num: 0800`, | 
|  | wantErr: `invalid "0800" as number or bool`, | 
|  | }, { | 
|  | in:      `num: true.`, | 
|  | wantErr: `invalid "true." as number or bool`, | 
|  | }, { | 
|  | in:      `num: .`, | 
|  | wantErr: `parsing ".": invalid syntax`, | 
|  | }, { | 
|  | in:      `num: -.`, | 
|  | wantErr: `parsing "-.": invalid syntax`, | 
|  | }, { | 
|  | in:      `num: 1e10000`, | 
|  | wantErr: `parsing "1e10000": value out of range`, | 
|  | }, { | 
|  | in:      `num: 99999999999999999999`, | 
|  | wantErr: `parsing "99999999999999999999": value out of range`, | 
|  | }, { | 
|  | in:      `num: -99999999999999999999`, | 
|  | wantErr: `parsing "-99999999999999999999": value out of range`, | 
|  | }, { | 
|  | in:      "x:  -", | 
|  | wantErr: `syntax error (line 1:5)`, | 
|  | }, { | 
|  | in:      "x:[\"💩\"x", | 
|  | wantErr: `syntax error (line 1:7)`, | 
|  | }, { | 
|  | in:      "x:\n\n[\"🔥🔥🔥\"x", | 
|  | wantErr: `syntax error (line 3:7)`, | 
|  | }, { | 
|  | in:      "x:[\"👍🏻👍🏿\"x", | 
|  | wantErr: `syntax error (line 1:10)`, // multi-rune emojis; could be column:8 | 
|  | }, { | 
|  | in: ` | 
|  | firstName : "John", | 
|  | lastName : "Smith" , | 
|  | isAlive : true, | 
|  | age : 27, | 
|  | address { # missing colon is okay for messages | 
|  | streetAddress : "21 2nd Street" , | 
|  | city : "New York" , | 
|  | state : "NY" , | 
|  | postalCode : "10021-3100" ; # trailing semicolon is okay | 
|  | }, | 
|  | phoneNumbers : [ { | 
|  | type : "home" , | 
|  | number : "212 555-1234" | 
|  | } , { | 
|  | type : "office" , | 
|  | number : "646 555-4567" | 
|  | } , { | 
|  | type : "mobile" , | 
|  | number : "123 456-7890" , # trailing comma is okay | 
|  | } ], | 
|  | children : [] , | 
|  | spouse : null`, | 
|  | wantVal: V(Msg{ | 
|  | {ID("firstName"), V("John")}, | 
|  | {ID("lastName"), V("Smith")}, | 
|  | {ID("isAlive"), V(true)}, | 
|  | {ID("age"), V(27.0)}, | 
|  | {ID("address"), V(Msg{ | 
|  | {ID("streetAddress"), V("21 2nd Street")}, | 
|  | {ID("city"), V("New York")}, | 
|  | {ID("state"), V("NY")}, | 
|  | {ID("postalCode"), V("10021-3100")}, | 
|  | })}, | 
|  | {ID("phoneNumbers"), V([]Value{ | 
|  | V(Msg{ | 
|  | {ID("type"), V("home")}, | 
|  | {ID("number"), V("212 555-1234")}, | 
|  | }), | 
|  | V(Msg{ | 
|  | {ID("type"), V("office")}, | 
|  | {ID("number"), V("646 555-4567")}, | 
|  | }), | 
|  | V(Msg{ | 
|  | {ID("type"), V("mobile")}, | 
|  | {ID("number"), V("123 456-7890")}, | 
|  | }), | 
|  | })}, | 
|  | {ID("children"), V([]Value{})}, | 
|  | {ID("spouse"), V(protoreflect.Name("null"))}, | 
|  | }), | 
|  | wantOut:        `firstName:"John" lastName:"Smith" isAlive:true age:27 address:{streetAddress:"21 2nd Street" city:"New York" state:"NY" postalCode:"10021-3100"} phoneNumbers:[{type:"home" number:"212 555-1234"},{type:"office" number:"646 555-4567"},{type:"mobile" number:"123 456-7890"}] children:[] spouse:null`, | 
|  | wantOutBracket: `firstName:"John" lastName:"Smith" isAlive:true age:27 address:<streetAddress:"21 2nd Street" city:"New York" state:"NY" postalCode:"10021-3100"> phoneNumbers:[<type:"home" number:"212 555-1234">,<type:"office" number:"646 555-4567">,<type:"mobile" number:"123 456-7890">] children:[] spouse:null`, | 
|  | wantOutIndent: `firstName: "John" | 
|  | lastName: "Smith" | 
|  | isAlive: true | 
|  | age: 27 | 
|  | address: { | 
|  | streetAddress: "21 2nd Street" | 
|  | city: "New York" | 
|  | state: "NY" | 
|  | postalCode: "10021-3100" | 
|  | } | 
|  | phoneNumbers: [ | 
|  | { | 
|  | type: "home" | 
|  | number: "212 555-1234" | 
|  | }, | 
|  | { | 
|  | type: "office" | 
|  | number: "646 555-4567" | 
|  | }, | 
|  | { | 
|  | type: "mobile" | 
|  | number: "123 456-7890" | 
|  | } | 
|  | ] | 
|  | children: [] | 
|  | spouse: null | 
|  | `, | 
|  | }} | 
|  |  | 
|  | opts := cmp.Options{ | 
|  | cmpopts.EquateEmpty(), | 
|  |  | 
|  | // Transform composites (List and Message). | 
|  | cmp.FilterValues(func(x, y Value) bool { | 
|  | return (x.Type() == List && y.Type() == List) || (x.Type() == Message && y.Type() == Message) | 
|  | }, cmp.Transformer("", func(v Value) interface{} { | 
|  | if v.Type() == List { | 
|  | return v.List() | 
|  | } else { | 
|  | return v.Message() | 
|  | } | 
|  | })), | 
|  |  | 
|  | // Compare scalars (Bool, Int, Uint, Float, String, Name). | 
|  | cmp.FilterValues(func(x, y Value) bool { | 
|  | return !(x.Type() == List && y.Type() == List) && !(x.Type() == Message && y.Type() == Message) | 
|  | }, cmp.Comparer(func(x, y Value) bool { | 
|  | if x.Type() == List || x.Type() == Message || y.Type() == List || y.Type() == Message { | 
|  | return false | 
|  | } | 
|  | // Ensure golden value is always in x variable. | 
|  | if len(x.raw) > 0 { | 
|  | x, y = y, x | 
|  | } | 
|  | switch x.Type() { | 
|  | case Bool: | 
|  | want, _ := x.Bool() | 
|  | got, ok := y.Bool() | 
|  | return got == want && ok | 
|  | case Int: | 
|  | want, _ := x.Int(true) | 
|  | got, ok := y.Int(want < math.MinInt32 || math.MaxInt32 < want) | 
|  | return got == want && ok | 
|  | case Uint: | 
|  | want, _ := x.Uint(true) | 
|  | got, ok := y.Uint(math.MaxUint32 < want) | 
|  | return got == want && ok | 
|  | case Float32, Float64: | 
|  | want, _ := x.Float(true) | 
|  | got, ok := y.Float(math.MaxFloat32 < math.Abs(want)) | 
|  | if math.IsNaN(got) || math.IsNaN(want) { | 
|  | return math.IsNaN(got) == math.IsNaN(want) | 
|  | } | 
|  | return got == want && ok | 
|  | case Name: | 
|  | want, _ := x.Name() | 
|  | got, ok := y.Name() | 
|  | return got == want && ok | 
|  | default: | 
|  | return x.String() == y.String() | 
|  | } | 
|  | })), | 
|  | } | 
|  | for _, tt := range tests { | 
|  | t.Run("", func(t *testing.T) { | 
|  | if tt.in != "" || tt.wantVal.Type() != 0 || tt.wantErr != "" { | 
|  | gotVal, err := Unmarshal([]byte(tt.in)) | 
|  | if err == nil { | 
|  | if tt.wantErr != "" { | 
|  | t.Errorf("Unmarshal(): got nil error, want %v", tt.wantErr) | 
|  | } | 
|  | } else { | 
|  | if tt.wantErr == "" { | 
|  | t.Errorf("Unmarshal(): got %v, want nil error", err) | 
|  | } else if !strings.Contains(err.Error(), tt.wantErr) { | 
|  | t.Errorf("Unmarshal(): got %v, want %v", err, tt.wantErr) | 
|  | } | 
|  | } | 
|  | if diff := cmp.Diff(gotVal, tt.wantVal, opts); diff != "" { | 
|  | t.Errorf("Unmarshal(): output mismatch (-got +want):\n%s", diff) | 
|  | } | 
|  | } | 
|  | if tt.wantOut != "" { | 
|  | gotOut, err := Marshal(tt.wantVal, "", [2]byte{0, 0}, false) | 
|  | if err != nil { | 
|  | t.Errorf("Marshal(): got %v, want nil error", err) | 
|  | } | 
|  | if string(gotOut) != tt.wantOut { | 
|  | t.Errorf("Marshal():\ngot:  %s\nwant: %s", gotOut, tt.wantOut) | 
|  | } | 
|  | } | 
|  | if tt.wantOutBracket != "" { | 
|  | gotOut, err := Marshal(tt.wantVal, "", [2]byte{'<', '>'}, false) | 
|  | if err != nil { | 
|  | t.Errorf("Marshal(Bracket): got %v, want nil error", err) | 
|  | } | 
|  | if string(gotOut) != tt.wantOutBracket { | 
|  | t.Errorf("Marshal(Bracket):\ngot:  %s\nwant: %s", gotOut, tt.wantOutBracket) | 
|  | } | 
|  | } | 
|  | if tt.wantOutASCII != "" { | 
|  | gotOut, err := Marshal(tt.wantVal, "", [2]byte{0, 0}, true) | 
|  | if err != nil { | 
|  | t.Errorf("Marshal(ASCII): got %v, want nil error", err) | 
|  | } | 
|  | if string(gotOut) != tt.wantOutASCII { | 
|  | t.Errorf("Marshal(ASCII):\ngot:  %s\nwant: %s", gotOut, tt.wantOutASCII) | 
|  | } | 
|  | } | 
|  | if tt.wantOutIndent != "" { | 
|  | gotOut, err := Marshal(tt.wantVal, "\t", [2]byte{0, 0}, false) | 
|  | if err != nil { | 
|  | t.Errorf("Marshal(Indent): got %v, want nil error", err) | 
|  | } | 
|  | if string(gotOut) != tt.wantOutIndent { | 
|  | t.Errorf("Marshal(Indent):\ngot:  %s\nwant: %s", gotOut, tt.wantOutIndent) | 
|  | } | 
|  | } | 
|  | }) | 
|  | } | 
|  | } |