k8s.io/kube-openapi@v0.0.0-20240228011516-70dd3763d340/pkg/internal/third_party/go-json-experiment/json/coder_test.go (about)

     1  // Copyright 2020 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package json
     6  
     7  import (
     8  	"bytes"
     9  	"errors"
    10  	"io"
    11  	"math"
    12  	"math/rand"
    13  	"path"
    14  	"strings"
    15  	"testing"
    16  )
    17  
    18  var (
    19  	zeroToken Token
    20  	zeroValue RawValue
    21  )
    22  
    23  // tokOrVal is either a Token or a RawValue.
    24  type tokOrVal interface{ Kind() Kind }
    25  
    26  type coderTestdataEntry struct {
    27  	name             testName
    28  	in               string
    29  	outCompacted     string
    30  	outEscaped       string // outCompacted if empty; escapes all runes in a string
    31  	outIndented      string // outCompacted if empty; uses "  " for indent prefix and "\t" for indent
    32  	outCanonicalized string // outCompacted if empty
    33  	tokens           []Token
    34  	pointers         []string
    35  }
    36  
    37  var coderTestdata = []coderTestdataEntry{{
    38  	name:         name("Null"),
    39  	in:           ` null `,
    40  	outCompacted: `null`,
    41  	tokens:       []Token{Null},
    42  	pointers:     []string{""},
    43  }, {
    44  	name:         name("False"),
    45  	in:           ` false `,
    46  	outCompacted: `false`,
    47  	tokens:       []Token{False},
    48  }, {
    49  	name:         name("True"),
    50  	in:           ` true `,
    51  	outCompacted: `true`,
    52  	tokens:       []Token{True},
    53  }, {
    54  	name:         name("EmptyString"),
    55  	in:           ` "" `,
    56  	outCompacted: `""`,
    57  	tokens:       []Token{String("")},
    58  }, {
    59  	name:         name("SimpleString"),
    60  	in:           ` "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" `,
    61  	outCompacted: `"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"`,
    62  	outEscaped:   `"\u0061\u0062\u0063\u0064\u0065\u0066\u0067\u0068\u0069\u006a\u006b\u006c\u006d\u006e\u006f\u0070\u0071\u0072\u0073\u0074\u0075\u0076\u0077\u0078\u0079\u007a\u0041\u0042\u0043\u0044\u0045\u0046\u0047\u0048\u0049\u004a\u004b\u004c\u004d\u004e\u004f\u0050\u0051\u0052\u0053\u0054\u0055\u0056\u0057\u0058\u0059\u005a"`,
    63  	tokens:       []Token{String("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")},
    64  }, {
    65  	name:             name("ComplicatedString"),
    66  	in:               " \"Hello, δΈ–η•Œ πŸŒŸβ˜…β˜†βœ©πŸŒ  " + "\u0080\u00f6\u20ac\ud799\ue000\ufb33\ufffd\U0001f602" + ` \ud800\udead \"\\\/\b\f\n\r\t \u0022\u005c\u002f\u0008\u000c\u000a\u000d\u0009" `,
    67  	outCompacted:     "\"Hello, δΈ–η•Œ πŸŒŸβ˜…β˜†βœ©πŸŒ  " + "\u0080\u00f6\u20ac\ud799\ue000\ufb33\ufffd\U0001f602" + " 𐊭 \\\"\\\\/\\b\\f\\n\\r\\t \\\"\\\\/\\b\\f\\n\\r\\t\"",
    68  	outEscaped:       `"\u0048\u0065\u006c\u006c\u006f\u002c\u0020\u4e16\u754c\u0020\ud83c\udf1f\u2605\u2606\u2729\ud83c\udf20\u0020\u0080\u00f6\u20ac\ud799\ue000\ufb33\ufffd\ud83d\ude02\u0020\ud800\udead\u0020\u0022\u005c\u002f\u0008\u000c\u000a\u000d\u0009\u0020\u0022\u005c\u002f\u0008\u000c\u000a\u000d\u0009"`,
    69  	outCanonicalized: `"Hello, δΈ–η•Œ πŸŒŸβ˜…β˜†βœ©πŸŒ  Β€ΓΆβ‚¬νž™ξ€€ο¬³οΏ½πŸ˜‚ 𐊭 \"\\/\b\f\n\r\t \"\\/\b\f\n\r\t"`,
    70  	tokens:           []Token{rawToken("\"Hello, δΈ–η•Œ πŸŒŸβ˜…β˜†βœ©πŸŒ  " + "\u0080\u00f6\u20ac\ud799\ue000\ufb33\ufffd\U0001f602" + " 𐊭 \\\"\\\\/\\b\\f\\n\\r\\t \\\"\\\\/\\b\\f\\n\\r\\t\"")},
    71  }, {
    72  	name:         name("ZeroNumber"),
    73  	in:           ` 0 `,
    74  	outCompacted: `0`,
    75  	tokens:       []Token{Uint(0)},
    76  }, {
    77  	name:         name("SimpleNumber"),
    78  	in:           ` 123456789 `,
    79  	outCompacted: `123456789`,
    80  	tokens:       []Token{Uint(123456789)},
    81  }, {
    82  	name:         name("NegativeNumber"),
    83  	in:           ` -123456789 `,
    84  	outCompacted: `-123456789`,
    85  	tokens:       []Token{Int(-123456789)},
    86  }, {
    87  	name:         name("FractionalNumber"),
    88  	in:           " 0.123456789 ",
    89  	outCompacted: `0.123456789`,
    90  	tokens:       []Token{Float(0.123456789)},
    91  }, {
    92  	name:             name("ExponentNumber"),
    93  	in:               " 0e12456789 ",
    94  	outCompacted:     `0e12456789`,
    95  	outCanonicalized: `0`,
    96  	tokens:           []Token{rawToken(`0e12456789`)},
    97  }, {
    98  	name:             name("ExponentNumberP"),
    99  	in:               " 0e+12456789 ",
   100  	outCompacted:     `0e+12456789`,
   101  	outCanonicalized: `0`,
   102  	tokens:           []Token{rawToken(`0e+12456789`)},
   103  }, {
   104  	name:             name("ExponentNumberN"),
   105  	in:               " 0e-12456789 ",
   106  	outCompacted:     `0e-12456789`,
   107  	outCanonicalized: `0`,
   108  	tokens:           []Token{rawToken(`0e-12456789`)},
   109  }, {
   110  	name:             name("ComplicatedNumber"),
   111  	in:               ` -123456789.987654321E+0123456789 `,
   112  	outCompacted:     `-123456789.987654321E+0123456789`,
   113  	outCanonicalized: `-1.7976931348623157e+308`,
   114  	tokens:           []Token{rawToken(`-123456789.987654321E+0123456789`)},
   115  }, {
   116  	name: name("Numbers"),
   117  	in: ` [
   118  		0, -0, 0.0, -0.0, 1.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001, 1e1000,
   119  		-5e-324, 1e+100, 1.7976931348623157e+308,
   120  		9007199254740990, 9007199254740991, 9007199254740992, 9007199254740993, 9007199254740994,
   121  		-9223372036854775808, 9223372036854775807, 0, 18446744073709551615
   122  	] `,
   123  	outCompacted: "[0,-0,0.0,-0.0,1.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001,1e1000,-5e-324,1e+100,1.7976931348623157e+308,9007199254740990,9007199254740991,9007199254740992,9007199254740993,9007199254740994,-9223372036854775808,9223372036854775807,0,18446744073709551615]",
   124  	outIndented: `[
   125  	    0,
   126  	    -0,
   127  	    0.0,
   128  	    -0.0,
   129  	    1.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001,
   130  	    1e1000,
   131  	    -5e-324,
   132  	    1e+100,
   133  	    1.7976931348623157e+308,
   134  	    9007199254740990,
   135  	    9007199254740991,
   136  	    9007199254740992,
   137  	    9007199254740993,
   138  	    9007199254740994,
   139  	    -9223372036854775808,
   140  	    9223372036854775807,
   141  	    0,
   142  	    18446744073709551615
   143  	]`,
   144  	outCanonicalized: `[0,0,0,0,1,1.7976931348623157e+308,-5e-324,1e+100,1.7976931348623157e+308,9007199254740990,9007199254740991,9007199254740992,9007199254740992,9007199254740994,-9223372036854776000,9223372036854776000,0,18446744073709552000]`,
   145  	tokens: []Token{
   146  		ArrayStart,
   147  		Float(0), Float(math.Copysign(0, -1)), rawToken(`0.0`), rawToken(`-0.0`), rawToken(`1.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001`), rawToken(`1e1000`),
   148  		Float(-5e-324), Float(1e100), Float(1.7976931348623157e+308),
   149  		Float(9007199254740990), Float(9007199254740991), Float(9007199254740992), rawToken(`9007199254740993`), rawToken(`9007199254740994`),
   150  		Int(minInt64), Int(maxInt64), Uint(minUint64), Uint(maxUint64),
   151  		ArrayEnd,
   152  	},
   153  	pointers: []string{
   154  		"", "/0", "/1", "/2", "/3", "/4", "/5", "/6", "/7", "/8", "/9", "/10", "/11", "/12", "/13", "/14", "/15", "/16", "/17", "",
   155  	},
   156  }, {
   157  	name:         name("ObjectN0"),
   158  	in:           ` { } `,
   159  	outCompacted: `{}`,
   160  	tokens:       []Token{ObjectStart, ObjectEnd},
   161  	pointers:     []string{"", ""},
   162  }, {
   163  	name:         name("ObjectN1"),
   164  	in:           ` { "0" : 0 } `,
   165  	outCompacted: `{"0":0}`,
   166  	outEscaped:   `{"\u0030":0}`,
   167  	outIndented: `{
   168  	    "0": 0
   169  	}`,
   170  	tokens:   []Token{ObjectStart, String("0"), Uint(0), ObjectEnd},
   171  	pointers: []string{"", "/0", "/0", ""},
   172  }, {
   173  	name:         name("ObjectN2"),
   174  	in:           ` { "0" : 0 , "1" : 1 } `,
   175  	outCompacted: `{"0":0,"1":1}`,
   176  	outEscaped:   `{"\u0030":0,"\u0031":1}`,
   177  	outIndented: `{
   178  	    "0": 0,
   179  	    "1": 1
   180  	}`,
   181  	tokens:   []Token{ObjectStart, String("0"), Uint(0), String("1"), Uint(1), ObjectEnd},
   182  	pointers: []string{"", "/0", "/0", "/1", "/1", ""},
   183  }, {
   184  	name:         name("ObjectNested"),
   185  	in:           ` { "0" : { "1" : { "2" : { "3" : { "4" : {  } } } } } } `,
   186  	outCompacted: `{"0":{"1":{"2":{"3":{"4":{}}}}}}`,
   187  	outEscaped:   `{"\u0030":{"\u0031":{"\u0032":{"\u0033":{"\u0034":{}}}}}}`,
   188  	outIndented: `{
   189  	    "0": {
   190  	        "1": {
   191  	            "2": {
   192  	                "3": {
   193  	                    "4": {}
   194  	                }
   195  	            }
   196  	        }
   197  	    }
   198  	}`,
   199  	tokens: []Token{ObjectStart, String("0"), ObjectStart, String("1"), ObjectStart, String("2"), ObjectStart, String("3"), ObjectStart, String("4"), ObjectStart, ObjectEnd, ObjectEnd, ObjectEnd, ObjectEnd, ObjectEnd, ObjectEnd},
   200  	pointers: []string{
   201  		"",
   202  		"/0", "/0",
   203  		"/0/1", "/0/1",
   204  		"/0/1/2", "/0/1/2",
   205  		"/0/1/2/3", "/0/1/2/3",
   206  		"/0/1/2/3/4", "/0/1/2/3/4",
   207  		"/0/1/2/3/4",
   208  		"/0/1/2/3",
   209  		"/0/1/2",
   210  		"/0/1",
   211  		"/0",
   212  		"",
   213  	},
   214  }, {
   215  	name: name("ObjectSuperNested"),
   216  	in: `{"": {
   217  		"44444": {
   218  			"6666666":  "ccccccc",
   219  			"77777777": "bb",
   220  			"555555":   "aaaa"
   221  		},
   222  		"0": {
   223  			"3333": "bbb",
   224  			"11":   "",
   225  			"222":  "aaaaa"
   226  		}
   227  	}}`,
   228  	outCompacted: `{"":{"44444":{"6666666":"ccccccc","77777777":"bb","555555":"aaaa"},"0":{"3333":"bbb","11":"","222":"aaaaa"}}}`,
   229  	outEscaped:   `{"":{"\u0034\u0034\u0034\u0034\u0034":{"\u0036\u0036\u0036\u0036\u0036\u0036\u0036":"\u0063\u0063\u0063\u0063\u0063\u0063\u0063","\u0037\u0037\u0037\u0037\u0037\u0037\u0037\u0037":"\u0062\u0062","\u0035\u0035\u0035\u0035\u0035\u0035":"\u0061\u0061\u0061\u0061"},"\u0030":{"\u0033\u0033\u0033\u0033":"\u0062\u0062\u0062","\u0031\u0031":"","\u0032\u0032\u0032":"\u0061\u0061\u0061\u0061\u0061"}}}`,
   230  	outIndented: `{
   231  	    "": {
   232  	        "44444": {
   233  	            "6666666": "ccccccc",
   234  	            "77777777": "bb",
   235  	            "555555": "aaaa"
   236  	        },
   237  	        "0": {
   238  	            "3333": "bbb",
   239  	            "11": "",
   240  	            "222": "aaaaa"
   241  	        }
   242  	    }
   243  	}`,
   244  	outCanonicalized: `{"":{"0":{"11":"","222":"aaaaa","3333":"bbb"},"44444":{"555555":"aaaa","6666666":"ccccccc","77777777":"bb"}}}`,
   245  	tokens: []Token{
   246  		ObjectStart,
   247  		String(""),
   248  		ObjectStart,
   249  		String("44444"),
   250  		ObjectStart,
   251  		String("6666666"), String("ccccccc"),
   252  		String("77777777"), String("bb"),
   253  		String("555555"), String("aaaa"),
   254  		ObjectEnd,
   255  		String("0"),
   256  		ObjectStart,
   257  		String("3333"), String("bbb"),
   258  		String("11"), String(""),
   259  		String("222"), String("aaaaa"),
   260  		ObjectEnd,
   261  		ObjectEnd,
   262  		ObjectEnd,
   263  	},
   264  	pointers: []string{
   265  		"",
   266  		"/", "/",
   267  		"//44444", "//44444",
   268  		"//44444/6666666", "//44444/6666666",
   269  		"//44444/77777777", "//44444/77777777",
   270  		"//44444/555555", "//44444/555555",
   271  		"//44444",
   272  		"//0", "//0",
   273  		"//0/3333", "//0/3333",
   274  		"//0/11", "//0/11",
   275  		"//0/222", "//0/222",
   276  		"//0",
   277  		"/",
   278  		"",
   279  	},
   280  }, {
   281  	name:         name("ArrayN0"),
   282  	in:           ` [ ] `,
   283  	outCompacted: `[]`,
   284  	tokens:       []Token{ArrayStart, ArrayEnd},
   285  	pointers:     []string{"", ""},
   286  }, {
   287  	name:         name("ArrayN1"),
   288  	in:           ` [ 0 ] `,
   289  	outCompacted: `[0]`,
   290  	outIndented: `[
   291  	    0
   292  	]`,
   293  	tokens:   []Token{ArrayStart, Uint(0), ArrayEnd},
   294  	pointers: []string{"", "/0", ""},
   295  }, {
   296  	name:         name("ArrayN2"),
   297  	in:           ` [ 0 , 1 ] `,
   298  	outCompacted: `[0,1]`,
   299  	outIndented: `[
   300  	    0,
   301  	    1
   302  	]`,
   303  	tokens: []Token{ArrayStart, Uint(0), Uint(1), ArrayEnd},
   304  }, {
   305  	name:         name("ArrayNested"),
   306  	in:           ` [ [ [ [ [ ] ] ] ] ] `,
   307  	outCompacted: `[[[[[]]]]]`,
   308  	outIndented: `[
   309  	    [
   310  	        [
   311  	            [
   312  	                []
   313  	            ]
   314  	        ]
   315  	    ]
   316  	]`,
   317  	tokens: []Token{ArrayStart, ArrayStart, ArrayStart, ArrayStart, ArrayStart, ArrayEnd, ArrayEnd, ArrayEnd, ArrayEnd, ArrayEnd},
   318  	pointers: []string{
   319  		"",
   320  		"/0",
   321  		"/0/0",
   322  		"/0/0/0",
   323  		"/0/0/0/0",
   324  		"/0/0/0/0",
   325  		"/0/0/0",
   326  		"/0/0",
   327  		"/0",
   328  		"",
   329  	},
   330  }, {
   331  	name: name("Everything"),
   332  	in: ` {
   333  		"literals" : [ null , false , true ],
   334  		"string" : "Hello, δΈ–η•Œ" ,
   335  		"number" : 3.14159 ,
   336  		"arrayN0" : [ ] ,
   337  		"arrayN1" : [ 0 ] ,
   338  		"arrayN2" : [ 0 , 1 ] ,
   339  		"objectN0" : { } ,
   340  		"objectN1" : { "0" : 0 } ,
   341  		"objectN2" : { "0" : 0 , "1" : 1 }
   342  	} `,
   343  	outCompacted: `{"literals":[null,false,true],"string":"Hello, δΈ–η•Œ","number":3.14159,"arrayN0":[],"arrayN1":[0],"arrayN2":[0,1],"objectN0":{},"objectN1":{"0":0},"objectN2":{"0":0,"1":1}}`,
   344  	outEscaped:   `{"\u006c\u0069\u0074\u0065\u0072\u0061\u006c\u0073":[null,false,true],"\u0073\u0074\u0072\u0069\u006e\u0067":"\u0048\u0065\u006c\u006c\u006f\u002c\u0020\u4e16\u754c","\u006e\u0075\u006d\u0062\u0065\u0072":3.14159,"\u0061\u0072\u0072\u0061\u0079\u004e\u0030":[],"\u0061\u0072\u0072\u0061\u0079\u004e\u0031":[0],"\u0061\u0072\u0072\u0061\u0079\u004e\u0032":[0,1],"\u006f\u0062\u006a\u0065\u0063\u0074\u004e\u0030":{},"\u006f\u0062\u006a\u0065\u0063\u0074\u004e\u0031":{"\u0030":0},"\u006f\u0062\u006a\u0065\u0063\u0074\u004e\u0032":{"\u0030":0,"\u0031":1}}`,
   345  	outIndented: `{
   346  	    "literals": [
   347  	        null,
   348  	        false,
   349  	        true
   350  	    ],
   351  	    "string": "Hello, δΈ–η•Œ",
   352  	    "number": 3.14159,
   353  	    "arrayN0": [],
   354  	    "arrayN1": [
   355  	        0
   356  	    ],
   357  	    "arrayN2": [
   358  	        0,
   359  	        1
   360  	    ],
   361  	    "objectN0": {},
   362  	    "objectN1": {
   363  	        "0": 0
   364  	    },
   365  	    "objectN2": {
   366  	        "0": 0,
   367  	        "1": 1
   368  	    }
   369  	}`,
   370  	outCanonicalized: `{"arrayN0":[],"arrayN1":[0],"arrayN2":[0,1],"literals":[null,false,true],"number":3.14159,"objectN0":{},"objectN1":{"0":0},"objectN2":{"0":0,"1":1},"string":"Hello, δΈ–η•Œ"}`,
   371  	tokens: []Token{
   372  		ObjectStart,
   373  		String("literals"), ArrayStart, Null, False, True, ArrayEnd,
   374  		String("string"), String("Hello, δΈ–η•Œ"),
   375  		String("number"), Float(3.14159),
   376  		String("arrayN0"), ArrayStart, ArrayEnd,
   377  		String("arrayN1"), ArrayStart, Uint(0), ArrayEnd,
   378  		String("arrayN2"), ArrayStart, Uint(0), Uint(1), ArrayEnd,
   379  		String("objectN0"), ObjectStart, ObjectEnd,
   380  		String("objectN1"), ObjectStart, String("0"), Uint(0), ObjectEnd,
   381  		String("objectN2"), ObjectStart, String("0"), Uint(0), String("1"), Uint(1), ObjectEnd,
   382  		ObjectEnd,
   383  	},
   384  	pointers: []string{
   385  		"",
   386  		"/literals", "/literals",
   387  		"/literals/0",
   388  		"/literals/1",
   389  		"/literals/2",
   390  		"/literals",
   391  		"/string", "/string",
   392  		"/number", "/number",
   393  		"/arrayN0", "/arrayN0", "/arrayN0",
   394  		"/arrayN1", "/arrayN1",
   395  		"/arrayN1/0",
   396  		"/arrayN1",
   397  		"/arrayN2", "/arrayN2",
   398  		"/arrayN2/0",
   399  		"/arrayN2/1",
   400  		"/arrayN2",
   401  		"/objectN0", "/objectN0", "/objectN0",
   402  		"/objectN1", "/objectN1",
   403  		"/objectN1/0", "/objectN1/0",
   404  		"/objectN1",
   405  		"/objectN2", "/objectN2",
   406  		"/objectN2/0", "/objectN2/0",
   407  		"/objectN2/1", "/objectN2/1",
   408  		"/objectN2",
   409  		"",
   410  	},
   411  }}
   412  
   413  // TestCoderInterleaved tests that we can interleave calls that operate on
   414  // tokens and raw values. The only error condition is trying to operate on a
   415  // raw value when the next token is an end of object or array.
   416  func TestCoderInterleaved(t *testing.T) {
   417  	for _, td := range coderTestdata {
   418  		// In TokenFirst and ValueFirst, alternate between tokens and values.
   419  		// In TokenDelims, only use tokens for object and array delimiters.
   420  		for _, modeName := range []string{"TokenFirst", "ValueFirst", "TokenDelims"} {
   421  			t.Run(path.Join(td.name.name, modeName), func(t *testing.T) {
   422  				testCoderInterleaved(t, td.name.where, modeName, td)
   423  			})
   424  		}
   425  	}
   426  }
   427  func testCoderInterleaved(t *testing.T, where pc, modeName string, td coderTestdataEntry) {
   428  	src := strings.NewReader(td.in)
   429  	dst := new(bytes.Buffer)
   430  	dec := NewDecoder(src)
   431  	enc := NewEncoder(dst)
   432  	tickTock := modeName == "TokenFirst"
   433  	for {
   434  		if modeName == "TokenDelims" {
   435  			switch dec.PeekKind() {
   436  			case '{', '}', '[', ']':
   437  				tickTock = true // as token
   438  			default:
   439  				tickTock = false // as value
   440  			}
   441  		}
   442  		if tickTock {
   443  			tok, err := dec.ReadToken()
   444  			if err != nil {
   445  				if err == io.EOF {
   446  					break
   447  				}
   448  				t.Fatalf("%s: Decoder.ReadToken error: %v", where, err)
   449  			}
   450  			if err := enc.WriteToken(tok); err != nil {
   451  				t.Fatalf("%s: Encoder.WriteToken error: %v", where, err)
   452  			}
   453  		} else {
   454  			val, err := dec.ReadValue()
   455  			if err != nil {
   456  				// It is a syntactic error to call ReadValue
   457  				// at the end of an object or array.
   458  				// Retry as a ReadToken call.
   459  				expectError := dec.PeekKind() == '}' || dec.PeekKind() == ']'
   460  				if expectError {
   461  					if !errors.As(err, new(*SyntacticError)) {
   462  						t.Fatalf("%s: Decoder.ReadToken error is %T, want %T", where, err, new(SyntacticError))
   463  					}
   464  					tickTock = !tickTock
   465  					continue
   466  				}
   467  
   468  				if err == io.EOF {
   469  					break
   470  				}
   471  				t.Fatalf("%s: Decoder.ReadValue error: %v", where, err)
   472  			}
   473  			if err := enc.WriteValue(val); err != nil {
   474  				t.Fatalf("%s: Encoder.WriteValue error: %v", where, err)
   475  			}
   476  		}
   477  		tickTock = !tickTock
   478  	}
   479  
   480  	got := dst.String()
   481  	want := td.outCompacted + "\n"
   482  	if got != want {
   483  		t.Fatalf("%s: output mismatch:\ngot  %q\nwant %q", where, got, want)
   484  	}
   485  }
   486  
   487  func TestCoderStackPointer(t *testing.T) {
   488  	tests := []struct {
   489  		token                        Token
   490  		wantWithRejectDuplicateNames string
   491  		wantWithAllowDuplicateNames  string
   492  	}{
   493  		{Null, "", ""},
   494  
   495  		{ArrayStart, "", ""},
   496  		{ArrayEnd, "", ""},
   497  
   498  		{ArrayStart, "", ""},
   499  		{Bool(true), "/0", "/0"},
   500  		{ArrayEnd, "", ""},
   501  
   502  		{ArrayStart, "", ""},
   503  		{String("hello"), "/0", "/0"},
   504  		{String("goodbye"), "/1", "/1"},
   505  		{ArrayEnd, "", ""},
   506  
   507  		{ObjectStart, "", ""},
   508  		{ObjectEnd, "", ""},
   509  
   510  		{ObjectStart, "", ""},
   511  		{String("hello"), "/hello", "/0"},
   512  		{String("goodbye"), "/hello", "/0"},
   513  		{ObjectEnd, "", ""},
   514  
   515  		{ObjectStart, "", ""},
   516  		{String(""), "/", "/0"},
   517  		{Null, "/", "/0"},
   518  		{String("0"), "/0", "/1"},
   519  		{Null, "/0", "/1"},
   520  		{String("~"), "/~0", "/2"},
   521  		{Null, "/~0", "/2"},
   522  		{String("/"), "/~1", "/3"},
   523  		{Null, "/~1", "/3"},
   524  		{String("a//b~/c/~d~~e"), "/a~1~1b~0~1c~1~0d~0~0e", "/4"},
   525  		{Null, "/a~1~1b~0~1c~1~0d~0~0e", "/4"},
   526  		{String(" \r\n\t"), "/ \r\n\t", "/5"},
   527  		{Null, "/ \r\n\t", "/5"},
   528  		{ObjectEnd, "", ""},
   529  
   530  		{ArrayStart, "", ""},
   531  		{ObjectStart, "/0", "/0"},
   532  		{String(""), "/0/", "/0/0"},
   533  		{ArrayStart, "/0/", "/0/0"},
   534  		{ObjectStart, "/0//0", "/0/0/0"},
   535  		{String("#"), "/0//0/#", "/0/0/0/0"},
   536  		{Null, "/0//0/#", "/0/0/0/0"},
   537  		{ObjectEnd, "/0//0", "/0/0/0"},
   538  		{ArrayEnd, "/0/", "/0/0"},
   539  		{ObjectEnd, "/0", "/0"},
   540  		{ArrayEnd, "", ""},
   541  	}
   542  
   543  	for _, allowDupes := range []bool{false, true} {
   544  		var name string
   545  		var want func(i int) string
   546  		switch allowDupes {
   547  		case false:
   548  			name = "RejectDuplicateNames"
   549  			want = func(i int) string { return tests[i].wantWithRejectDuplicateNames }
   550  		case true:
   551  			name = "AllowDuplicateNames"
   552  			want = func(i int) string { return tests[i].wantWithAllowDuplicateNames }
   553  		}
   554  
   555  		t.Run(name, func(t *testing.T) {
   556  			bb := new(bytes.Buffer)
   557  
   558  			enc := EncodeOptions{AllowDuplicateNames: allowDupes}.NewEncoder(bb)
   559  			for i, tt := range tests {
   560  				if err := enc.WriteToken(tt.token); err != nil {
   561  					t.Fatalf("%d: Encoder.WriteToken error: %v", i, err)
   562  				}
   563  				if got := enc.StackPointer(); got != want(i) {
   564  					t.Fatalf("%d: Encoder.StackPointer = %v, want %v", i, got, want(i))
   565  				}
   566  			}
   567  
   568  			dec := DecodeOptions{AllowDuplicateNames: allowDupes}.NewDecoder(bb)
   569  			for i := range tests {
   570  				if _, err := dec.ReadToken(); err != nil {
   571  					t.Fatalf("%d: Decoder.ReadToken error: %v", i, err)
   572  				}
   573  				if got := dec.StackPointer(); got != want(i) {
   574  					t.Fatalf("%d: Decoder.StackPointer = %v, want %v", i, got, want(i))
   575  				}
   576  			}
   577  		})
   578  	}
   579  }
   580  
   581  func TestCoderBufferGrowth(t *testing.T) {
   582  	// The growth rate of the internal buffer should be exponential,
   583  	// but should not grow unbounded.
   584  	checkGrowth := func(ns []int) {
   585  		t.Helper()
   586  		var sumBytes, sumRates, numGrows float64
   587  		prev := ns[0]
   588  		for i := 1; i < len(ns)-1; i++ {
   589  			n := ns[i]
   590  			if n != prev {
   591  				sumRates += float64(n) / float64(prev)
   592  				numGrows++
   593  				prev = n
   594  			}
   595  			if n > 1<<20 {
   596  				t.Fatalf("single Read/Write too large: %d", n)
   597  			}
   598  			sumBytes += float64(n)
   599  		}
   600  		if mean := sumBytes / float64(len(ns)); mean < 1<<10 {
   601  			t.Fatalf("average Read/Write too small: %0.1f", mean)
   602  		}
   603  		switch mean := sumRates / numGrows; {
   604  		case mean < 1.25:
   605  			t.Fatalf("average growth rate too slow: %0.3f", mean)
   606  		case mean > 2.00:
   607  			t.Fatalf("average growth rate too fast: %0.3f", mean)
   608  		}
   609  	}
   610  
   611  	bb := &bytesBuffer{new(bytes.Buffer)}
   612  
   613  	var writeSizes []int
   614  	if err := MarshalFull(WriterFunc(func(b []byte) (int, error) {
   615  		n, err := bb.Write(b)
   616  		writeSizes = append(writeSizes, n)
   617  		return n, err
   618  	}), make([]struct{}, 1e6)); err != nil {
   619  		t.Fatalf("MarshalFull error: %v", err)
   620  	}
   621  	checkGrowth(writeSizes)
   622  
   623  	var readSizes []int
   624  	if err := UnmarshalFull(ReaderFunc(func(b []byte) (int, error) {
   625  		n, err := bb.Read(b)
   626  		readSizes = append(readSizes, n)
   627  		return n, err
   628  	}), new([]struct{})); err != nil {
   629  		t.Fatalf("UnmarshalFull error: %v", err)
   630  	}
   631  	checkGrowth(readSizes)
   632  }
   633  
   634  type ReaderFunc func([]byte) (int, error)
   635  
   636  func (f ReaderFunc) Read(b []byte) (int, error) { return f(b) }
   637  
   638  type WriterFunc func([]byte) (int, error)
   639  
   640  func (f WriterFunc) Write(b []byte) (int, error) { return f(b) }
   641  
   642  // FaultyBuffer implements io.Reader and io.Writer.
   643  // It may process fewer bytes than the provided buffer
   644  // and may randomly return an error.
   645  type FaultyBuffer struct {
   646  	B []byte
   647  
   648  	// MaxBytes is the maximum number of bytes read/written.
   649  	// A random number of bytes within [0, MaxBytes] are processed.
   650  	// A non-positive value is treated as infinity.
   651  	MaxBytes int
   652  
   653  	// MayError specifies whether to randomly provide this error.
   654  	// Even if an error is returned, no bytes are dropped.
   655  	MayError error
   656  
   657  	// Rand to use for pseudo-random behavior.
   658  	// If nil, it will be initialized with rand.NewSource(0).
   659  	Rand rand.Source
   660  }
   661  
   662  func (p *FaultyBuffer) Read(b []byte) (int, error) {
   663  	b = b[:copy(b[:p.mayTruncate(len(b))], p.B)]
   664  	p.B = p.B[len(b):]
   665  	if len(p.B) == 0 && (len(b) == 0 || p.randN(2) == 0) {
   666  		return len(b), io.EOF
   667  	}
   668  	return len(b), p.mayError()
   669  }
   670  
   671  func (p *FaultyBuffer) Write(b []byte) (int, error) {
   672  	b2 := b[:p.mayTruncate(len(b))]
   673  	p.B = append(p.B, b2...)
   674  	if len(b2) < len(b) {
   675  		return len(b2), io.ErrShortWrite
   676  	}
   677  	return len(b2), p.mayError()
   678  }
   679  
   680  // mayTruncate may return a value between [0, n].
   681  func (p *FaultyBuffer) mayTruncate(n int) int {
   682  	if p.MaxBytes > 0 {
   683  		if n > p.MaxBytes {
   684  			n = p.MaxBytes
   685  		}
   686  		return p.randN(n + 1)
   687  	}
   688  	return n
   689  }
   690  
   691  // mayError may return a non-nil error.
   692  func (p *FaultyBuffer) mayError() error {
   693  	if p.MayError != nil && p.randN(2) == 0 {
   694  		return p.MayError
   695  	}
   696  	return nil
   697  }
   698  
   699  func (p *FaultyBuffer) randN(n int) int {
   700  	if p.Rand == nil {
   701  		p.Rand = rand.NewSource(0)
   702  	}
   703  	return int(p.Rand.Int63() % int64(n))
   704  }