1
2
3
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
24 type tokOrVal interface{ Kind() Kind }
25
26 type coderTestdataEntry struct {
27 name testName
28 in string
29 outCompacted string
30 outEscaped string
31 outIndented string
32 outCanonicalized string
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
414
415
416 func TestCoderInterleaved(t *testing.T) {
417 for _, td := range coderTestdata {
418
419
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
438 default:
439 tickTock = false
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
457
458
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
583
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
643
644
645 type FaultyBuffer struct {
646 B []byte
647
648
649
650
651 MaxBytes int
652
653
654
655 MayError error
656
657
658
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
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
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 }
705
View as plain text