123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557 |
- // Copyright 2019 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_test
- import (
- "math"
- "strings"
- "testing"
- "unicode/utf8"
- "github.com/google/go-cmp/cmp"
- "google.golang.org/protobuf/internal/detrand"
- "google.golang.org/protobuf/internal/encoding/text"
- )
- // Disable detrand to enable direct comparisons on outputs.
- func init() { detrand.Disable() }
- func TestEncoder(t *testing.T) {
- tests := []encoderTestCase{
- {
- desc: "no-opt",
- write: func(e *text.Encoder) {},
- wantOut: ``,
- wantOutIndent: ``,
- },
- {
- desc: "true",
- write: func(e *text.Encoder) {
- e.WriteName("bool")
- e.WriteBool(true)
- },
- wantOut: `bool:true`,
- wantOutIndent: `bool: true`,
- },
- {
- desc: "false",
- write: func(e *text.Encoder) {
- e.WriteName("bool")
- e.WriteBool(false)
- },
- wantOut: `bool:false`,
- wantOutIndent: `bool: false`,
- },
- {
- desc: "bracket name",
- write: func(e *text.Encoder) {
- e.WriteName("[extension]")
- e.WriteString("hello")
- },
- wantOut: `[extension]:"hello"`,
- wantOutIndent: `[extension]: "hello"`,
- },
- {
- desc: "numeric name",
- write: func(e *text.Encoder) {
- e.WriteName("01234")
- e.WriteString("hello")
- },
- wantOut: `01234:"hello"`,
- wantOutIndent: `01234: "hello"`,
- },
- {
- desc: "string",
- write: func(e *text.Encoder) {
- e.WriteName("str")
- e.WriteString("hello world")
- },
- wantOut: `str:"hello world"`,
- wantOutIndent: `str: "hello world"`,
- },
- {
- desc: "enum",
- write: func(e *text.Encoder) {
- e.WriteName("enum")
- e.WriteLiteral("ENUM_VALUE")
- },
- wantOut: `enum:ENUM_VALUE`,
- wantOutIndent: `enum: ENUM_VALUE`,
- },
- {
- desc: "float64",
- write: func(e *text.Encoder) {
- e.WriteName("float64")
- e.WriteFloat(1.0199999809265137, 64)
- },
- wantOut: `float64:1.0199999809265137`,
- wantOutIndent: `float64: 1.0199999809265137`,
- },
- {
- desc: "float64 max value",
- write: func(e *text.Encoder) {
- e.WriteName("float64")
- e.WriteFloat(math.MaxFloat64, 64)
- },
- wantOut: `float64:1.7976931348623157e+308`,
- wantOutIndent: `float64: 1.7976931348623157e+308`,
- },
- {
- desc: "float64 min value",
- write: func(e *text.Encoder) {
- e.WriteName("float64")
- e.WriteFloat(-math.MaxFloat64, 64)
- },
- wantOut: `float64:-1.7976931348623157e+308`,
- wantOutIndent: `float64: -1.7976931348623157e+308`,
- },
- {
- desc: "float64 nan",
- write: func(e *text.Encoder) {
- e.WriteName("float64")
- e.WriteFloat(math.NaN(), 64)
- },
- wantOut: `float64:nan`,
- wantOutIndent: `float64: nan`,
- },
- {
- desc: "float64 inf",
- write: func(e *text.Encoder) {
- e.WriteName("float64")
- e.WriteFloat(math.Inf(+1), 64)
- },
- wantOut: `float64:inf`,
- wantOutIndent: `float64: inf`,
- },
- {
- desc: "float64 -inf",
- write: func(e *text.Encoder) {
- e.WriteName("float64")
- e.WriteFloat(math.Inf(-1), 64)
- },
- wantOut: `float64:-inf`,
- wantOutIndent: `float64: -inf`,
- },
- {
- desc: "float64 negative zero",
- write: func(e *text.Encoder) {
- e.WriteName("float64")
- e.WriteFloat(math.Copysign(0, -1), 64)
- },
- wantOut: `float64:-0`,
- wantOutIndent: `float64: -0`,
- },
- {
- desc: "float32",
- write: func(e *text.Encoder) {
- e.WriteName("float")
- e.WriteFloat(1.02, 32)
- },
- wantOut: `float:1.02`,
- wantOutIndent: `float: 1.02`,
- },
- {
- desc: "float32 max value",
- write: func(e *text.Encoder) {
- e.WriteName("float32")
- e.WriteFloat(math.MaxFloat32, 32)
- },
- wantOut: `float32:3.4028235e+38`,
- wantOutIndent: `float32: 3.4028235e+38`,
- },
- {
- desc: "float32 nan",
- write: func(e *text.Encoder) {
- e.WriteName("float32")
- e.WriteFloat(math.NaN(), 32)
- },
- wantOut: `float32:nan`,
- wantOutIndent: `float32: nan`,
- },
- {
- desc: "float32 inf",
- write: func(e *text.Encoder) {
- e.WriteName("float32")
- e.WriteFloat(math.Inf(+1), 32)
- },
- wantOut: `float32:inf`,
- wantOutIndent: `float32: inf`,
- },
- {
- desc: "float32 -inf",
- write: func(e *text.Encoder) {
- e.WriteName("float32")
- e.WriteFloat(math.Inf(-1), 32)
- },
- wantOut: `float32:-inf`,
- wantOutIndent: `float32: -inf`,
- },
- {
- desc: "float32 negative zero",
- write: func(e *text.Encoder) {
- e.WriteName("float32")
- e.WriteFloat(math.Copysign(0, -1), 32)
- },
- wantOut: `float32:-0`,
- wantOutIndent: `float32: -0`,
- },
- {
- desc: "int64 max value",
- write: func(e *text.Encoder) {
- e.WriteName("int")
- e.WriteInt(math.MaxInt64)
- },
- wantOut: `int:9223372036854775807`,
- wantOutIndent: `int: 9223372036854775807`,
- },
- {
- desc: "int64 min value",
- write: func(e *text.Encoder) {
- e.WriteName("int")
- e.WriteInt(math.MinInt64)
- },
- wantOut: `int:-9223372036854775808`,
- wantOutIndent: `int: -9223372036854775808`,
- },
- {
- desc: "uint",
- write: func(e *text.Encoder) {
- e.WriteName("uint")
- e.WriteUint(math.MaxUint64)
- },
- wantOut: `uint:18446744073709551615`,
- wantOutIndent: `uint: 18446744073709551615`,
- },
- {
- desc: "empty message field",
- write: func(e *text.Encoder) {
- e.WriteName("m")
- e.StartMessage()
- e.EndMessage()
- },
- wantOut: `m:{}`,
- wantOutIndent: `m: {}`,
- },
- {
- desc: "multiple fields",
- write: func(e *text.Encoder) {
- e.WriteName("bool")
- e.WriteBool(true)
- e.WriteName("str")
- e.WriteString("hello")
- e.WriteName("str")
- e.WriteString("world")
- e.WriteName("m")
- e.StartMessage()
- e.EndMessage()
- e.WriteName("[int]")
- e.WriteInt(49)
- e.WriteName("float64")
- e.WriteFloat(1.00023e4, 64)
- e.WriteName("101")
- e.WriteString("unknown")
- },
- wantOut: `bool:true str:"hello" str:"world" m:{} [int]:49 float64:10002.3 101:"unknown"`,
- wantOutIndent: `bool: true
- str: "hello"
- str: "world"
- m: {}
- [int]: 49
- float64: 10002.3
- 101: "unknown"`,
- },
- {
- desc: "populated message fields",
- write: func(e *text.Encoder) {
- e.WriteName("m1")
- e.StartMessage()
- {
- e.WriteName("str")
- e.WriteString("hello")
- }
- e.EndMessage()
- e.WriteName("bool")
- e.WriteBool(true)
- e.WriteName("m2")
- e.StartMessage()
- {
- e.WriteName("str")
- e.WriteString("world")
- e.WriteName("m2-1")
- e.StartMessage()
- e.EndMessage()
- e.WriteName("m2-2")
- e.StartMessage()
- {
- e.WriteName("[int]")
- e.WriteInt(49)
- }
- e.EndMessage()
- e.WriteName("float64")
- e.WriteFloat(1.00023e4, 64)
- }
- e.EndMessage()
- e.WriteName("101")
- e.WriteString("unknown")
- },
- wantOut: `m1:{str:"hello"} bool:true m2:{str:"world" m2-1:{} m2-2:{[int]:49} float64:10002.3} 101:"unknown"`,
- wantOutIndent: `m1: {
- str: "hello"
- }
- bool: true
- m2: {
- str: "world"
- m2-1: {}
- m2-2: {
- [int]: 49
- }
- float64: 10002.3
- }
- 101: "unknown"`,
- },
- }
- for _, tc := range tests {
- t.Run(tc.desc, func(t *testing.T) {
- runEncoderTest(t, tc, [2]byte{})
- // Test using the angle brackets.
- // Testcases should not contain characters '{' and '}'.
- tc.wantOut = replaceDelims(tc.wantOut)
- tc.wantOutIndent = replaceDelims(tc.wantOutIndent)
- runEncoderTest(t, tc, [2]byte{'<', '>'})
- })
- }
- }
- type encoderTestCase struct {
- desc string
- write func(*text.Encoder)
- wantOut string
- wantOutIndent string
- }
- func runEncoderTest(t *testing.T, tc encoderTestCase, delims [2]byte) {
- t.Helper()
- if tc.wantOut != "" {
- enc, err := text.NewEncoder(nil, "", delims, false)
- if err != nil {
- t.Fatalf("NewEncoder returned error: %v", err)
- }
- tc.write(enc)
- got := string(enc.Bytes())
- if got != tc.wantOut {
- t.Errorf("(compact)\n<got>\n%v\n<want>\n%v\n", got, tc.wantOut)
- }
- }
- if tc.wantOutIndent != "" {
- enc, err := text.NewEncoder(nil, "\t", delims, false)
- if err != nil {
- t.Fatalf("NewEncoder returned error: %v", err)
- }
- tc.write(enc)
- got, want := string(enc.Bytes()), tc.wantOutIndent
- if got != want {
- t.Errorf("(multi-line)\n<got>\n%v\n<want>\n%v\n<diff -want +got>\n%v\n",
- got, want, cmp.Diff(want, got))
- }
- }
- }
- func replaceDelims(s string) string {
- s = strings.Replace(s, "{", "<", -1)
- return strings.Replace(s, "}", ">", -1)
- }
- // Test for UTF-8 and ASCII outputs.
- func TestEncodeStrings(t *testing.T) {
- tests := []struct {
- in string
- wantOut string
- wantOutASCII string
- }{
- {
- in: `"`,
- wantOut: `"\""`,
- },
- {
- in: `'`,
- wantOut: `"'"`,
- },
- {
- in: "hello\u1234world",
- wantOut: "\"hello\u1234world\"",
- wantOutASCII: `"hello\u1234world"`,
- },
- {
- // String that has as few escaped characters as possible.
- in: func() string {
- var b []byte
- for i := rune(0); i <= 0x00a0; i++ {
- switch i {
- case 0, '\\', '\n', '\'': // these must be escaped, so ignore them
- default:
- var r [utf8.UTFMax]byte
- n := utf8.EncodeRune(r[:], i)
- b = append(b, r[:n]...)
- }
- }
- return string(b)
- }(),
- wantOut: `"\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\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089\u008a\u008b\u008c\u008d\u008e\u008f\u0090\u0091\u0092\u0093\u0094\u0095\u0096\u0097\u0098\u0099\u009a\u009b\u009c\u009d\u009e\u009f` + "\u00a0" + `"`,
- wantOutASCII: `"\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\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089\u008a\u008b\u008c\u008d\u008e\u008f\u0090\u0091\u0092\u0093\u0094\u0095\u0096\u0097\u0098\u0099\u009a\u009b\u009c\u009d\u009e\u009f\u00a0"`,
- },
- {
- // Valid UTF-8 wire encoding of the RuneError rune.
- in: string(utf8.RuneError),
- wantOut: `"` + string(utf8.RuneError) + `"`,
- wantOutASCII: `"\ufffd"`,
- },
- {
- in: "\"'\\?\a\b\n\r\t\v\f\x01\nS\n\xab\x12\uab8f\U0010ffff",
- wantOut: `"\"'\\?\x07\x08\n\r\t\x0b\x0c\x01\nS\n\xab\x12` + "\uab8f\U0010ffff" + `"`,
- wantOutASCII: `"\"'\\?\x07\x08\n\r\t\x0b\x0c\x01\nS\n\xab\x12\uab8f\U0010ffff"`,
- },
- {
- in: "\001x",
- wantOut: `"\x01x"`,
- wantOutASCII: `"\x01x"`,
- },
- {
- in: "\012x",
- wantOut: `"\nx"`,
- wantOutASCII: `"\nx"`,
- },
- {
- in: "\123x",
- wantOut: `"Sx"`,
- wantOutASCII: `"Sx"`,
- },
- {
- in: "\1234x",
- wantOut: `"S4x"`,
- wantOutASCII: `"S4x"`,
- },
- {
- in: "\001",
- wantOut: `"\x01"`,
- wantOutASCII: `"\x01"`,
- },
- {
- in: "\012",
- wantOut: `"\n"`,
- wantOutASCII: `"\n"`,
- },
- {
- in: "\123",
- wantOut: `"S"`,
- wantOutASCII: `"S"`,
- },
- {
- in: "\1234",
- wantOut: `"S4"`,
- wantOutASCII: `"S4"`,
- },
- {
- in: "\377",
- wantOut: `"\xff"`,
- wantOutASCII: `"\xff"`,
- },
- {
- in: "\x0fx",
- wantOut: `"\x0fx"`,
- wantOutASCII: `"\x0fx"`,
- },
- {
- in: "\xffx",
- wantOut: `"\xffx"`,
- wantOutASCII: `"\xffx"`,
- },
- {
- in: "\xfffx",
- wantOut: `"\xfffx"`,
- wantOutASCII: `"\xfffx"`,
- },
- {
- in: "\x0f",
- wantOut: `"\x0f"`,
- wantOutASCII: `"\x0f"`,
- },
- {
- in: "\x7f",
- wantOut: `"\x7f"`,
- wantOutASCII: `"\x7f"`,
- },
- {
- in: "\xff",
- wantOut: `"\xff"`,
- wantOutASCII: `"\xff"`,
- },
- {
- in: "\xfff",
- wantOut: `"\xfff"`,
- wantOutASCII: `"\xfff"`,
- },
- }
- for _, tc := range tests {
- t.Run("", func(t *testing.T) {
- if tc.wantOut != "" {
- runEncodeStringsTest(t, tc.in, tc.wantOut, false)
- }
- if tc.wantOutASCII != "" {
- runEncodeStringsTest(t, tc.in, tc.wantOutASCII, true)
- }
- })
- }
- }
- func runEncodeStringsTest(t *testing.T, in string, want string, outputASCII bool) {
- t.Helper()
- charType := "UTF-8"
- if outputASCII {
- charType = "ASCII"
- }
- enc, err := text.NewEncoder(nil, "", [2]byte{}, outputASCII)
- if err != nil {
- t.Fatalf("[%s] NewEncoder returned error: %v", charType, err)
- }
- enc.WriteString(in)
- got := string(enc.Bytes())
- if got != want {
- t.Errorf("[%s] WriteString(%q)\n<got>\n%v\n<want>\n%v\n", charType, in, got, want)
- }
- }
- func TestReset(t *testing.T) {
- enc, err := text.NewEncoder(nil, "\t", [2]byte{}, false)
- if err != nil {
- t.Fatalf("NewEncoder returned error: %v", err)
- }
- enc.WriteName("foo")
- pos := enc.Snapshot()
- // Attempt to write a message value.
- enc.StartMessage()
- enc.WriteName("bar")
- enc.WriteUint(10)
- // Reset the value and decided to write a string value instead.
- enc.Reset(pos)
- enc.WriteString("0123456789")
- got := string(enc.Bytes())
- want := `foo: "0123456789"`
- if got != want {
- t.Errorf("Reset did not restore given position:\n<got>\n%v\n<want>\n%v\n", got, want)
- }
- }
|