1
2
3
4
5
6
7 package jsontext
8
9 import (
10 "bytes"
11 "errors"
12 "io"
13 "path"
14 "slices"
15 "testing"
16
17 "encoding/json/internal/jsonflags"
18 "encoding/json/internal/jsontest"
19 "encoding/json/internal/jsonwire"
20 )
21
22
23 func TestEncoder(t *testing.T) {
24 for _, td := range coderTestdata {
25 for _, formatName := range []string{"Compact", "Indented"} {
26 for _, typeName := range []string{"Token", "Value", "TokenDelims"} {
27 t.Run(path.Join(td.name.Name, typeName, formatName), func(t *testing.T) {
28 testEncoder(t, td.name.Where, formatName, typeName, td)
29 })
30 }
31 }
32 }
33 }
34 func testEncoder(t *testing.T, where jsontest.CasePos, formatName, typeName string, td coderTestdataEntry) {
35 var want string
36 var opts []Options
37 dst := new(bytes.Buffer)
38 opts = append(opts, jsonflags.OmitTopLevelNewline|1)
39 want = td.outCompacted
40 switch formatName {
41 case "Indented":
42 opts = append(opts, Multiline(true))
43 opts = append(opts, WithIndentPrefix("\t"))
44 opts = append(opts, WithIndent(" "))
45 if td.outIndented != "" {
46 want = td.outIndented
47 }
48 }
49 enc := NewEncoder(dst, opts...)
50
51 switch typeName {
52 case "Token":
53 var pointers []Pointer
54 for _, tok := range td.tokens {
55 if err := enc.WriteToken(tok); err != nil {
56 t.Fatalf("%s: Encoder.WriteToken error: %v", where, err)
57 }
58 if td.pointers != nil {
59 pointers = append(pointers, enc.StackPointer())
60 }
61 }
62 if !slices.Equal(pointers, td.pointers) {
63 t.Fatalf("%s: pointers mismatch:\ngot %q\nwant %q", where, pointers, td.pointers)
64 }
65 case "Value":
66 if err := enc.WriteValue(Value(td.in)); err != nil {
67 t.Fatalf("%s: Encoder.WriteValue error: %v", where, err)
68 }
69 case "TokenDelims":
70
71 for _, tok := range td.tokens {
72 switch tok.Kind() {
73 case '{', '}', '[', ']':
74 if err := enc.WriteToken(tok); err != nil {
75 t.Fatalf("%s: Encoder.WriteToken error: %v", where, err)
76 }
77 default:
78 val := Value(tok.String())
79 if tok.Kind() == '"' {
80 val, _ = jsonwire.AppendQuote(nil, tok.String(), &jsonflags.Flags{})
81 }
82 if err := enc.WriteValue(val); err != nil {
83 t.Fatalf("%s: Encoder.WriteValue error: %v", where, err)
84 }
85 }
86 }
87 }
88
89 got := dst.String()
90 if got != want {
91 t.Errorf("%s: output mismatch:\ngot %q\nwant %q", where, got, want)
92 }
93 }
94
95
96 func TestFaultyEncoder(t *testing.T) {
97 for _, td := range coderTestdata {
98 for _, typeName := range []string{"Token", "Value"} {
99 t.Run(path.Join(td.name.Name, typeName), func(t *testing.T) {
100 testFaultyEncoder(t, td.name.Where, typeName, td)
101 })
102 }
103 }
104 }
105 func testFaultyEncoder(t *testing.T, where jsontest.CasePos, typeName string, td coderTestdataEntry) {
106 b := &FaultyBuffer{
107 MaxBytes: 1,
108 MayError: io.ErrShortWrite,
109 }
110
111
112
113
114
115
116 enc := NewEncoder(b)
117 switch typeName {
118 case "Token":
119 for i, tok := range td.tokens {
120 err := enc.WriteToken(tok)
121 if err != nil && !errors.Is(err, io.ErrShortWrite) {
122 t.Fatalf("%s: %d: Encoder.WriteToken error: %v", where, i, err)
123 }
124 }
125 case "Value":
126 err := enc.WriteValue(Value(td.in))
127 if err != nil && !errors.Is(err, io.ErrShortWrite) {
128 t.Fatalf("%s: Encoder.WriteValue error: %v", where, err)
129 }
130 }
131 gotOutput := string(append(b.B, enc.s.unflushedBuffer()...))
132 wantOutput := td.outCompacted + "\n"
133 if gotOutput != wantOutput {
134 t.Fatalf("%s: output mismatch:\ngot %s\nwant %s", where, gotOutput, wantOutput)
135 }
136 }
137
138 type encoderMethodCall struct {
139 in tokOrVal
140 wantErr error
141 wantPointer Pointer
142 }
143
144 var encoderErrorTestdata = []struct {
145 name jsontest.CaseName
146 opts []Options
147 calls []encoderMethodCall
148 wantOut string
149 }{{
150 name: jsontest.Name("InvalidToken"),
151 calls: []encoderMethodCall{
152 {zeroToken, E(errInvalidToken), ""},
153 },
154 }, {
155 name: jsontest.Name("InvalidValue"),
156 calls: []encoderMethodCall{
157 {Value(`#`), newInvalidCharacterError("#", "at start of value"), ""},
158 },
159 }, {
160 name: jsontest.Name("InvalidValue/DoubleZero"),
161 calls: []encoderMethodCall{
162 {Value(`00`), newInvalidCharacterError("0", "after top-level value").withPos(`0`, ""), ""},
163 },
164 }, {
165 name: jsontest.Name("TruncatedValue"),
166 calls: []encoderMethodCall{
167 {zeroValue, E(io.ErrUnexpectedEOF).withPos("", ""), ""},
168 },
169 }, {
170 name: jsontest.Name("TruncatedNull"),
171 calls: []encoderMethodCall{
172 {Value(`nul`), E(io.ErrUnexpectedEOF).withPos("nul", ""), ""},
173 },
174 }, {
175 name: jsontest.Name("InvalidNull"),
176 calls: []encoderMethodCall{
177 {Value(`nulL`), newInvalidCharacterError("L", "in literal null (expecting 'l')").withPos(`nul`, ""), ""},
178 },
179 }, {
180 name: jsontest.Name("TruncatedFalse"),
181 calls: []encoderMethodCall{
182 {Value(`fals`), E(io.ErrUnexpectedEOF).withPos("fals", ""), ""},
183 },
184 }, {
185 name: jsontest.Name("InvalidFalse"),
186 calls: []encoderMethodCall{
187 {Value(`falsE`), newInvalidCharacterError("E", "in literal false (expecting 'e')").withPos(`fals`, ""), ""},
188 },
189 }, {
190 name: jsontest.Name("TruncatedTrue"),
191 calls: []encoderMethodCall{
192 {Value(`tru`), E(io.ErrUnexpectedEOF).withPos(`tru`, ""), ""},
193 },
194 }, {
195 name: jsontest.Name("InvalidTrue"),
196 calls: []encoderMethodCall{
197 {Value(`truE`), newInvalidCharacterError("E", "in literal true (expecting 'e')").withPos(`tru`, ""), ""},
198 },
199 }, {
200 name: jsontest.Name("TruncatedString"),
201 calls: []encoderMethodCall{
202 {Value(`"star`), E(io.ErrUnexpectedEOF).withPos(`"star`, ""), ""},
203 },
204 }, {
205 name: jsontest.Name("InvalidString"),
206 calls: []encoderMethodCall{
207 {Value(`"ok` + "\x00"), newInvalidCharacterError("\x00", `in string (expecting non-control character)`).withPos(`"ok`, ""), ""},
208 },
209 }, {
210 name: jsontest.Name("ValidString/AllowInvalidUTF8/Token"),
211 opts: []Options{AllowInvalidUTF8(true)},
212 calls: []encoderMethodCall{
213 {String("living\xde\xad\xbe\xef"), nil, ""},
214 },
215 wantOut: "\"living\xde\xad\ufffd\ufffd\"\n",
216 }, {
217 name: jsontest.Name("ValidString/AllowInvalidUTF8/Value"),
218 opts: []Options{AllowInvalidUTF8(true)},
219 calls: []encoderMethodCall{
220 {Value("\"living\xde\xad\xbe\xef\""), nil, ""},
221 },
222 wantOut: "\"living\xde\xad\ufffd\ufffd\"\n",
223 }, {
224 name: jsontest.Name("InvalidString/RejectInvalidUTF8"),
225 opts: []Options{AllowInvalidUTF8(false)},
226 calls: []encoderMethodCall{
227 {String("living\xde\xad\xbe\xef"), E(jsonwire.ErrInvalidUTF8), ""},
228 {Value("\"living\xde\xad\xbe\xef\""), E(jsonwire.ErrInvalidUTF8).withPos("\"living\xde\xad", ""), ""},
229 {BeginObject, nil, ""},
230 {String("name"), nil, ""},
231 {BeginArray, nil, ""},
232 {String("living\xde\xad\xbe\xef"), E(jsonwire.ErrInvalidUTF8).withPos(`{"name":[`, "/name/0"), ""},
233 {Value("\"living\xde\xad\xbe\xef\""), E(jsonwire.ErrInvalidUTF8).withPos("{\"name\":[\"living\xde\xad", "/name/0"), ""},
234 },
235 wantOut: `{"name":[`,
236 }, {
237 name: jsontest.Name("TruncatedNumber"),
238 calls: []encoderMethodCall{
239 {Value(`0.`), E(io.ErrUnexpectedEOF).withPos("0", ""), ""},
240 },
241 }, {
242 name: jsontest.Name("InvalidNumber"),
243 calls: []encoderMethodCall{
244 {Value(`0.e`), newInvalidCharacterError("e", "in number (expecting digit)").withPos(`0.`, ""), ""},
245 },
246 }, {
247 name: jsontest.Name("TruncatedObject/AfterStart"),
248 calls: []encoderMethodCall{
249 {Value(`{`), E(io.ErrUnexpectedEOF).withPos("{", ""), ""},
250 },
251 }, {
252 name: jsontest.Name("TruncatedObject/AfterName"),
253 calls: []encoderMethodCall{
254 {Value(`{"X"`), E(io.ErrUnexpectedEOF).withPos(`{"X"`, "/X"), ""},
255 },
256 }, {
257 name: jsontest.Name("TruncatedObject/AfterColon"),
258 calls: []encoderMethodCall{
259 {Value(`{"X":`), E(io.ErrUnexpectedEOF).withPos(`{"X":`, "/X"), ""},
260 },
261 }, {
262 name: jsontest.Name("TruncatedObject/AfterValue"),
263 calls: []encoderMethodCall{
264 {Value(`{"0":0`), E(io.ErrUnexpectedEOF).withPos(`{"0":0`, ""), ""},
265 },
266 }, {
267 name: jsontest.Name("TruncatedObject/AfterComma"),
268 calls: []encoderMethodCall{
269 {Value(`{"0":0,`), E(io.ErrUnexpectedEOF).withPos(`{"0":0,`, ""), ""},
270 },
271 }, {
272 name: jsontest.Name("InvalidObject/MissingColon"),
273 calls: []encoderMethodCall{
274 {Value(` { "fizz" "buzz" } `), newInvalidCharacterError("\"", "after object name (expecting ':')").withPos(` { "fizz" `, "/fizz"), ""},
275 {Value(` { "fizz" , "buzz" } `), newInvalidCharacterError(",", "after object name (expecting ':')").withPos(` { "fizz" `, "/fizz"), ""},
276 },
277 }, {
278 name: jsontest.Name("InvalidObject/MissingComma"),
279 calls: []encoderMethodCall{
280 {Value(` { "fizz" : "buzz" "gazz" } `), newInvalidCharacterError("\"", "after object value (expecting ',' or '}')").withPos(` { "fizz" : "buzz" `, ""), ""},
281 {Value(` { "fizz" : "buzz" : "gazz" } `), newInvalidCharacterError(":", "after object value (expecting ',' or '}')").withPos(` { "fizz" : "buzz" `, ""), ""},
282 },
283 }, {
284 name: jsontest.Name("InvalidObject/ExtraComma"),
285 calls: []encoderMethodCall{
286 {Value(` { , } `), newInvalidCharacterError(",", `at start of string (expecting '"')`).withPos(` { `, ""), ""},
287 {Value(` { "fizz" : "buzz" , } `), newInvalidCharacterError("}", `at start of string (expecting '"')`).withPos(` { "fizz" : "buzz" , `, ""), ""},
288 },
289 }, {
290 name: jsontest.Name("InvalidObject/InvalidName"),
291 calls: []encoderMethodCall{
292 {Value(`{ null }`), newInvalidCharacterError("n", `at start of string (expecting '"')`).withPos(`{ `, ""), ""},
293 {Value(`{ false }`), newInvalidCharacterError("f", `at start of string (expecting '"')`).withPos(`{ `, ""), ""},
294 {Value(`{ true }`), newInvalidCharacterError("t", `at start of string (expecting '"')`).withPos(`{ `, ""), ""},
295 {Value(`{ 0 }`), newInvalidCharacterError("0", `at start of string (expecting '"')`).withPos(`{ `, ""), ""},
296 {Value(`{ {} }`), newInvalidCharacterError("{", `at start of string (expecting '"')`).withPos(`{ `, ""), ""},
297 {Value(`{ [] }`), newInvalidCharacterError("[", `at start of string (expecting '"')`).withPos(`{ `, ""), ""},
298 {BeginObject, nil, ""},
299 {Null, E(ErrNonStringName).withPos(`{`, ""), ""},
300 {Value(`null`), E(ErrNonStringName).withPos(`{`, ""), ""},
301 {False, E(ErrNonStringName).withPos(`{`, ""), ""},
302 {Value(`false`), E(ErrNonStringName).withPos(`{`, ""), ""},
303 {True, E(ErrNonStringName).withPos(`{`, ""), ""},
304 {Value(`true`), E(ErrNonStringName).withPos(`{`, ""), ""},
305 {Uint(0), E(ErrNonStringName).withPos(`{`, ""), ""},
306 {Value(`0`), E(ErrNonStringName).withPos(`{`, ""), ""},
307 {BeginObject, E(ErrNonStringName).withPos(`{`, ""), ""},
308 {Value(`{}`), E(ErrNonStringName).withPos(`{`, ""), ""},
309 {BeginArray, E(ErrNonStringName).withPos(`{`, ""), ""},
310 {Value(`[]`), E(ErrNonStringName).withPos(`{`, ""), ""},
311 {EndObject, nil, ""},
312 },
313 wantOut: "{}\n",
314 }, {
315 name: jsontest.Name("InvalidObject/InvalidValue"),
316 calls: []encoderMethodCall{
317 {Value(`{ "0": x }`), newInvalidCharacterError("x", `at start of value`).withPos(`{ "0": `, "/0"), ""},
318 },
319 }, {
320 name: jsontest.Name("InvalidObject/MismatchingDelim"),
321 calls: []encoderMethodCall{
322 {Value(` { ] `), newInvalidCharacterError("]", `at start of string (expecting '"')`).withPos(` { `, ""), ""},
323 {Value(` { "0":0 ] `), newInvalidCharacterError("]", `after object value (expecting ',' or '}')`).withPos(` { "0":0 `, ""), ""},
324 {BeginObject, nil, ""},
325 {EndArray, E(errMismatchDelim).withPos(`{`, ""), ""},
326 {Value(`]`), newInvalidCharacterError("]", "at start of value").withPos(`{`, ""), ""},
327 {EndObject, nil, ""},
328 },
329 wantOut: "{}\n",
330 }, {
331 name: jsontest.Name("ValidObject/UniqueNames"),
332 calls: []encoderMethodCall{
333 {BeginObject, nil, ""},
334 {String("0"), nil, ""},
335 {Uint(0), nil, ""},
336 {String("1"), nil, ""},
337 {Uint(1), nil, ""},
338 {EndObject, nil, ""},
339 {Value(` { "0" : 0 , "1" : 1 } `), nil, ""},
340 },
341 wantOut: `{"0":0,"1":1}` + "\n" + `{"0":0,"1":1}` + "\n",
342 }, {
343 name: jsontest.Name("ValidObject/DuplicateNames"),
344 opts: []Options{AllowDuplicateNames(true)},
345 calls: []encoderMethodCall{
346 {BeginObject, nil, ""},
347 {String("0"), nil, ""},
348 {Uint(0), nil, ""},
349 {String("0"), nil, ""},
350 {Uint(0), nil, ""},
351 {EndObject, nil, ""},
352 {Value(` { "0" : 0 , "0" : 0 } `), nil, ""},
353 },
354 wantOut: `{"0":0,"0":0}` + "\n" + `{"0":0,"0":0}` + "\n",
355 }, {
356 name: jsontest.Name("InvalidObject/DuplicateNames"),
357 calls: []encoderMethodCall{
358 {BeginObject, nil, ""},
359 {String("X"), nil, ""},
360 {BeginObject, nil, ""},
361 {EndObject, nil, ""},
362 {String("X"), E(ErrDuplicateName).withPos(`{"X":{},`, "/X"), "/X"},
363 {Value(`"X"`), E(ErrDuplicateName).withPos(`{"X":{},`, "/X"), "/X"},
364 {String("Y"), nil, ""},
365 {BeginObject, nil, ""},
366 {EndObject, nil, ""},
367 {String("X"), E(ErrDuplicateName).withPos(`{"X":{},"Y":{},`, "/X"), "/Y"},
368 {Value(`"X"`), E(ErrDuplicateName).withPos(`{"X":{},"Y":{},`, "/X"), "/Y"},
369 {String("Y"), E(ErrDuplicateName).withPos(`{"X":{},"Y":{},`, "/Y"), "/Y"},
370 {Value(`"Y"`), E(ErrDuplicateName).withPos(`{"X":{},"Y":{},`, "/Y"), "/Y"},
371 {EndObject, nil, ""},
372 {Value(` { "X" : 0 , "Y" : 1 , "X" : 0 } `), E(ErrDuplicateName).withPos(`{"X":{},"Y":{}}`+"\n"+` { "X" : 0 , "Y" : 1 , `, "/X"), ""},
373 },
374 wantOut: `{"X":{},"Y":{}}` + "\n",
375 }, {
376 name: jsontest.Name("TruncatedArray/AfterStart"),
377 calls: []encoderMethodCall{
378 {Value(`[`), E(io.ErrUnexpectedEOF).withPos(`[`, ""), ""},
379 },
380 }, {
381 name: jsontest.Name("TruncatedArray/AfterValue"),
382 calls: []encoderMethodCall{
383 {Value(`[0`), E(io.ErrUnexpectedEOF).withPos(`[0`, ""), ""},
384 },
385 }, {
386 name: jsontest.Name("TruncatedArray/AfterComma"),
387 calls: []encoderMethodCall{
388 {Value(`[0,`), E(io.ErrUnexpectedEOF).withPos(`[0,`, ""), ""},
389 },
390 }, {
391 name: jsontest.Name("TruncatedArray/MissingComma"),
392 calls: []encoderMethodCall{
393 {Value(` [ "fizz" "buzz" ] `), newInvalidCharacterError("\"", "after array value (expecting ',' or ']')").withPos(` [ "fizz" `, ""), ""},
394 },
395 }, {
396 name: jsontest.Name("InvalidArray/MismatchingDelim"),
397 calls: []encoderMethodCall{
398 {Value(` [ } `), newInvalidCharacterError("}", `at start of value`).withPos(` [ `, "/0"), ""},
399 {BeginArray, nil, ""},
400 {EndObject, E(errMismatchDelim).withPos(`[`, "/0"), ""},
401 {Value(`}`), newInvalidCharacterError("}", "at start of value").withPos(`[`, "/0"), ""},
402 {EndArray, nil, ""},
403 },
404 wantOut: "[]\n",
405 }, {
406 name: jsontest.Name("Format/Object/SpaceAfterColon"),
407 opts: []Options{SpaceAfterColon(true)},
408 calls: []encoderMethodCall{{Value(`{"fizz":"buzz","wizz":"wuzz"}`), nil, ""}},
409 wantOut: "{\"fizz\": \"buzz\",\"wizz\": \"wuzz\"}\n",
410 }, {
411 name: jsontest.Name("Format/Object/SpaceAfterComma"),
412 opts: []Options{SpaceAfterComma(true)},
413 calls: []encoderMethodCall{{Value(`{"fizz":"buzz","wizz":"wuzz"}`), nil, ""}},
414 wantOut: "{\"fizz\":\"buzz\", \"wizz\":\"wuzz\"}\n",
415 }, {
416 name: jsontest.Name("Format/Object/SpaceAfterColonAndComma"),
417 opts: []Options{SpaceAfterColon(true), SpaceAfterComma(true)},
418 calls: []encoderMethodCall{{Value(`{"fizz":"buzz","wizz":"wuzz"}`), nil, ""}},
419 wantOut: "{\"fizz\": \"buzz\", \"wizz\": \"wuzz\"}\n",
420 }, {
421 name: jsontest.Name("Format/Object/NoSpaceAfterColon+SpaceAfterComma+Multiline"),
422 opts: []Options{SpaceAfterColon(false), SpaceAfterComma(true), Multiline(true)},
423 calls: []encoderMethodCall{{Value(`{"fizz":"buzz","wizz":"wuzz"}`), nil, ""}},
424 wantOut: "{\n\t\"fizz\":\"buzz\", \n\t\"wizz\":\"wuzz\"\n}\n",
425 }, {
426 name: jsontest.Name("Format/Array/SpaceAfterComma"),
427 opts: []Options{SpaceAfterComma(true)},
428 calls: []encoderMethodCall{{Value(`["fizz","buzz"]`), nil, ""}},
429 wantOut: "[\"fizz\", \"buzz\"]\n",
430 }, {
431 name: jsontest.Name("Format/Array/NoSpaceAfterComma+Multiline"),
432 opts: []Options{SpaceAfterComma(false), Multiline(true)},
433 calls: []encoderMethodCall{{Value(`["fizz","buzz"]`), nil, ""}},
434 wantOut: "[\n\t\"fizz\",\n\t\"buzz\"\n]\n",
435 }, {
436 name: jsontest.Name("Format/ReorderWithWhitespace"),
437 opts: []Options{
438 AllowDuplicateNames(true),
439 AllowInvalidUTF8(true),
440 ReorderRawObjects(true),
441 SpaceAfterComma(true),
442 SpaceAfterColon(false),
443 Multiline(true),
444 WithIndentPrefix(" "),
445 WithIndent("\t"),
446 PreserveRawStrings(true),
447 },
448 calls: []encoderMethodCall{
449 {BeginArray, nil, ""},
450 {BeginArray, nil, ""},
451 {Value(` { "fizz" : "buzz" ,
452 "zip" : {
453 "x` + "\xfd" + `x" : 123 , "x` + "\xff" + `x" : 123, "x` + "\xfe" + `x" : 123
454 },
455 "zap" : {
456 "xxx" : 333, "xxx": 1, "xxx": 22
457 },
458 "alpha" : "bravo" } `), nil, ""},
459 {EndArray, nil, ""},
460 {EndArray, nil, ""},
461 },
462 wantOut: "[\n \t[\n \t\t{\n \t\t\t\"alpha\":\"bravo\", \n \t\t\t\"fizz\":\"buzz\", \n \t\t\t\"zap\":{\n \t\t\t\t\"xxx\":1, \n \t\t\t\t\"xxx\":22, \n \t\t\t\t\"xxx\":333\n \t\t\t}, \n \t\t\t\"zip\":{\n \t\t\t\t\"x\xfdx\":123, \n \t\t\t\t\"x\xfex\":123, \n \t\t\t\t\"x\xffx\":123\n \t\t\t}\n \t\t}\n \t]\n ]\n",
463 }, {
464 name: jsontest.Name("Format/CanonicalizeRawInts"),
465 opts: []Options{CanonicalizeRawInts(true), SpaceAfterComma(true)},
466 calls: []encoderMethodCall{
467 {Value(`[0.100,5.0,1E6,-9223372036854775808,-10,-1,-0,0,1,10,9223372036854775807]`), nil, ""},
468 },
469 wantOut: "[0.100, 5.0, 1E6, -9223372036854776000, -10, -1, 0, 0, 1, 10, 9223372036854776000]\n",
470 }, {
471 name: jsontest.Name("Format/CanonicalizeRawFloats"),
472 opts: []Options{CanonicalizeRawFloats(true), SpaceAfterComma(true)},
473 calls: []encoderMethodCall{
474 {Value(`[0.100,5.0,1E6,-9223372036854775808,-10,-1,-0,0,1,10,9223372036854775807]`), nil, ""},
475 },
476 wantOut: "[0.1, 5, 1000000, -9223372036854775808, -10, -1, 0, 0, 1, 10, 9223372036854775807]\n",
477 }, {
478 name: jsontest.Name("ErrorPosition"),
479 calls: []encoderMethodCall{
480 {Value(` "a` + "\xff" + `0" `), E(jsonwire.ErrInvalidUTF8).withPos(` "a`, ""), ""},
481 {String(`a` + "\xff" + `0`), E(jsonwire.ErrInvalidUTF8).withPos(``, ""), ""},
482 },
483 }, {
484 name: jsontest.Name("ErrorPosition/0"),
485 calls: []encoderMethodCall{
486 {Value(` [ "a` + "\xff" + `1" ] `), E(jsonwire.ErrInvalidUTF8).withPos(` [ "a`, "/0"), ""},
487 {BeginArray, nil, ""},
488 {Value(` "a` + "\xff" + `1" `), E(jsonwire.ErrInvalidUTF8).withPos(`[ "a`, "/0"), ""},
489 {String(`a` + "\xff" + `1`), E(jsonwire.ErrInvalidUTF8).withPos(`[`, "/0"), ""},
490 },
491 wantOut: `[`,
492 }, {
493 name: jsontest.Name("ErrorPosition/1"),
494 calls: []encoderMethodCall{
495 {Value(` [ "a1" , "b` + "\xff" + `1" ] `), E(jsonwire.ErrInvalidUTF8).withPos(` [ "a1" , "b`, "/1"), ""},
496 {BeginArray, nil, ""},
497 {String("a1"), nil, ""},
498 {Value(` "b` + "\xff" + `1" `), E(jsonwire.ErrInvalidUTF8).withPos(`["a1", "b`, "/1"), ""},
499 {String(`b` + "\xff" + `1`), E(jsonwire.ErrInvalidUTF8).withPos(`["a1",`, "/1"), ""},
500 },
501 wantOut: `["a1"`,
502 }, {
503 name: jsontest.Name("ErrorPosition/0/0"),
504 calls: []encoderMethodCall{
505 {Value(` [ [ "a` + "\xff" + `2" ] ] `), E(jsonwire.ErrInvalidUTF8).withPos(` [ [ "a`, "/0/0"), ""},
506 {BeginArray, nil, ""},
507 {Value(` [ "a` + "\xff" + `2" ] `), E(jsonwire.ErrInvalidUTF8).withPos(`[ [ "a`, "/0/0"), ""},
508 {BeginArray, nil, "/0"},
509 {Value(` "a` + "\xff" + `2" `), E(jsonwire.ErrInvalidUTF8).withPos(`[[ "a`, "/0/0"), "/0"},
510 {String(`a` + "\xff" + `2`), E(jsonwire.ErrInvalidUTF8).withPos(`[[`, "/0/0"), "/0"},
511 },
512 wantOut: `[[`,
513 }, {
514 name: jsontest.Name("ErrorPosition/1/0"),
515 calls: []encoderMethodCall{
516 {Value(` [ "a1" , [ "a` + "\xff" + `2" ] ] `), E(jsonwire.ErrInvalidUTF8).withPos(` [ "a1" , [ "a`, "/1/0"), ""},
517 {BeginArray, nil, ""},
518 {String("a1"), nil, "/0"},
519 {Value(` [ "a` + "\xff" + `2" ] `), E(jsonwire.ErrInvalidUTF8).withPos(`["a1", [ "a`, "/1/0"), ""},
520 {BeginArray, nil, "/1"},
521 {Value(` "a` + "\xff" + `2" `), E(jsonwire.ErrInvalidUTF8).withPos(`["a1",[ "a`, "/1/0"), "/1"},
522 {String(`a` + "\xff" + `2`), E(jsonwire.ErrInvalidUTF8).withPos(`["a1",[`, "/1/0"), "/1"},
523 },
524 wantOut: `["a1",[`,
525 }, {
526 name: jsontest.Name("ErrorPosition/0/1"),
527 calls: []encoderMethodCall{
528 {Value(` [ [ "a2" , "b` + "\xff" + `2" ] ] `), E(jsonwire.ErrInvalidUTF8).withPos(` [ [ "a2" , "b`, "/0/1"), ""},
529 {BeginArray, nil, ""},
530 {Value(` [ "a2" , "b` + "\xff" + `2" ] `), E(jsonwire.ErrInvalidUTF8).withPos(`[ [ "a2" , "b`, "/0/1"), ""},
531 {BeginArray, nil, "/0"},
532 {String("a2"), nil, "/0/0"},
533 {Value(` "b` + "\xff" + `2" `), E(jsonwire.ErrInvalidUTF8).withPos(`[["a2", "b`, "/0/1"), "/0/0"},
534 {String(`b` + "\xff" + `2`), E(jsonwire.ErrInvalidUTF8).withPos(`[["a2",`, "/0/1"), "/0/0"},
535 },
536 wantOut: `[["a2"`,
537 }, {
538 name: jsontest.Name("ErrorPosition/1/1"),
539 calls: []encoderMethodCall{
540 {Value(` [ "a1" , [ "a2" , "b` + "\xff" + `2" ] ] `), E(jsonwire.ErrInvalidUTF8).withPos(` [ "a1" , [ "a2" , "b`, "/1/1"), ""},
541 {BeginArray, nil, ""},
542 {String("a1"), nil, "/0"},
543 {Value(` [ "a2" , "b` + "\xff" + `2" ] `), E(jsonwire.ErrInvalidUTF8).withPos(`["a1", [ "a2" , "b`, "/1/1"), ""},
544 {BeginArray, nil, "/1"},
545 {String("a2"), nil, "/1/0"},
546 {Value(` "b` + "\xff" + `2" `), E(jsonwire.ErrInvalidUTF8).withPos(`["a1",["a2", "b`, "/1/1"), "/1/0"},
547 {String(`b` + "\xff" + `2`), E(jsonwire.ErrInvalidUTF8).withPos(`["a1",["a2",`, "/1/1"), "/1/0"},
548 },
549 wantOut: `["a1",["a2"`,
550 }, {
551 name: jsontest.Name("ErrorPosition/a1-"),
552 calls: []encoderMethodCall{
553 {Value(` { "a` + "\xff" + `1" : "b1" } `), E(jsonwire.ErrInvalidUTF8).withPos(` { "a`, ""), ""},
554 {BeginObject, nil, ""},
555 {Value(` "a` + "\xff" + `1" `), E(jsonwire.ErrInvalidUTF8).withPos(`{ "a`, ""), ""},
556 {String(`a` + "\xff" + `1`), E(jsonwire.ErrInvalidUTF8).withPos(`{`, ""), ""},
557 },
558 wantOut: `{`,
559 }, {
560 name: jsontest.Name("ErrorPosition/a1"),
561 calls: []encoderMethodCall{
562 {Value(` { "a1" : "b` + "\xff" + `1" } `), E(jsonwire.ErrInvalidUTF8).withPos(` { "a1" : "b`, "/a1"), ""},
563 {BeginObject, nil, ""},
564 {String("a1"), nil, "/a1"},
565 {Value(` "b` + "\xff" + `1" `), E(jsonwire.ErrInvalidUTF8).withPos(`{"a1": "b`, "/a1"), ""},
566 {String(`b` + "\xff" + `1`), E(jsonwire.ErrInvalidUTF8).withPos(`{"a1":`, "/a1"), ""},
567 },
568 wantOut: `{"a1"`,
569 }, {
570 name: jsontest.Name("ErrorPosition/c1-"),
571 calls: []encoderMethodCall{
572 {Value(` { "a1" : "b1" , "c` + "\xff" + `1" : "d1" } `), E(jsonwire.ErrInvalidUTF8).withPos(` { "a1" : "b1" , "c`, ""), ""},
573 {BeginObject, nil, ""},
574 {String("a1"), nil, "/a1"},
575 {String("b1"), nil, "/a1"},
576 {Value(` "c` + "\xff" + `1" `), E(jsonwire.ErrInvalidUTF8).withPos(`{"a1":"b1": "c`, ""), "/a1"},
577 {String(`c` + "\xff" + `1`), E(jsonwire.ErrInvalidUTF8).withPos(`{"a1":"b1":`, ""), "/a1"},
578 },
579 wantOut: `{"a1":"b1"`,
580 }, {
581 name: jsontest.Name("ErrorPosition/c1"),
582 calls: []encoderMethodCall{
583 {Value(` { "a1" : "b1" , "c1" : "d` + "\xff" + `1" } `), E(jsonwire.ErrInvalidUTF8).withPos(` { "a1" : "b1" , "c1" : "d`, "/c1"), ""},
584 {BeginObject, nil, ""},
585 {String("a1"), nil, "/a1"},
586 {String("b1"), nil, "/a1"},
587 {String("c1"), nil, "/c1"},
588 {Value(` "d` + "\xff" + `1" `), E(jsonwire.ErrInvalidUTF8).withPos(`{"a1":"b1":"c1": "d`, "/c1"), "/c1"},
589 {String(`d` + "\xff" + `1`), E(jsonwire.ErrInvalidUTF8).withPos(`{"a1":"b1":"c1":`, "/c1"), "/c1"},
590 },
591 wantOut: `{"a1":"b1","c1"`,
592 }, {
593 name: jsontest.Name("ErrorPosition/a1/a2-"),
594 calls: []encoderMethodCall{
595 {Value(` { "a1" : { "a` + "\xff" + `2" : "b2" } } `), E(jsonwire.ErrInvalidUTF8).withPos(` { "a1" : { "a`, "/a1"), ""},
596 {BeginObject, nil, ""},
597 {String("a1"), nil, "/a1"},
598 {Value(` { "a` + "\xff" + `2" : "b2" } `), E(jsonwire.ErrInvalidUTF8).withPos(`{"a1": { "a`, "/a1"), ""},
599 {BeginObject, nil, "/a1"},
600 {Value(` "a` + "\xff" + `2" `), E(jsonwire.ErrInvalidUTF8).withPos(`{"a1":{ "a`, "/a1"), "/a1"},
601 {String(`a` + "\xff" + `2`), E(jsonwire.ErrInvalidUTF8).withPos(`{"a1":{`, "/a1"), "/a1"},
602 },
603 wantOut: `{"a1":{`,
604 }, {
605 name: jsontest.Name("ErrorPosition/a1/a2"),
606 calls: []encoderMethodCall{
607 {Value(` { "a1" : { "a2" : "b` + "\xff" + `2" } } `), E(jsonwire.ErrInvalidUTF8).withPos(` { "a1" : { "a2" : "b`, "/a1/a2"), ""},
608 {BeginObject, nil, ""},
609 {String("a1"), nil, "/a1"},
610 {Value(` { "a2" : "b` + "\xff" + `2" } `), E(jsonwire.ErrInvalidUTF8).withPos(`{"a1": { "a2" : "b`, "/a1/a2"), ""},
611 {BeginObject, nil, "/a1"},
612 {String("a2"), nil, "/a1/a2"},
613 {Value(` "b` + "\xff" + `2" `), E(jsonwire.ErrInvalidUTF8).withPos(`{"a1":{"a2": "b`, "/a1/a2"), "/a1/a2"},
614 {String(`b` + "\xff" + `2`), E(jsonwire.ErrInvalidUTF8).withPos(`{"a1":{"a2":`, "/a1/a2"), "/a1/a2"},
615 },
616 wantOut: `{"a1":{"a2"`,
617 }, {
618 name: jsontest.Name("ErrorPosition/a1/c2-"),
619 calls: []encoderMethodCall{
620 {Value(` { "a1" : { "a2" : "b2" , "c` + "\xff" + `2" : "d2" } } `), E(jsonwire.ErrInvalidUTF8).withPos(` { "a1" : { "a2" : "b2" , "c`, "/a1"), ""},
621 {BeginObject, nil, ""},
622 {String("a1"), nil, "/a1"},
623 {BeginObject, nil, "/a1"},
624 {String("a2"), nil, "/a1/a2"},
625 {String("b2"), nil, "/a1/a2"},
626 {Value(` "c` + "\xff" + `2" `), E(jsonwire.ErrInvalidUTF8).withPos(`{"a1":{"a2":"b2", "c`, "/a1"), "/a1/a2"},
627 {String(`c` + "\xff" + `2`), E(jsonwire.ErrInvalidUTF8).withPos(`{"a1":{"a2":"b2",`, "/a1"), "/a1/a2"},
628 },
629 wantOut: `{"a1":{"a2":"b2"`,
630 }, {
631 name: jsontest.Name("ErrorPosition/a1/c2"),
632 calls: []encoderMethodCall{
633 {Value(` { "a1" : { "a2" : "b2" , "c2" : "d` + "\xff" + `2" } } `), E(jsonwire.ErrInvalidUTF8).withPos(` { "a1" : { "a2" : "b2" , "c2" : "d`, "/a1/c2"), ""},
634 {BeginObject, nil, ""},
635 {String("a1"), nil, "/a1"},
636 {Value(` { "a2" : "b2" , "c2" : "d` + "\xff" + `2" } `), E(jsonwire.ErrInvalidUTF8).withPos(`{"a1": { "a2" : "b2" , "c2" : "d`, "/a1/c2"), ""},
637 {BeginObject, nil, ""},
638 {String("a2"), nil, "/a1/a2"},
639 {String("b2"), nil, "/a1/a2"},
640 {String("c2"), nil, "/a1/c2"},
641 {Value(` "d` + "\xff" + `2" `), E(jsonwire.ErrInvalidUTF8).withPos(`{"a1":{"a2":"b2","c2": "d`, "/a1/c2"), "/a1/c2"},
642 {String(`d` + "\xff" + `2`), E(jsonwire.ErrInvalidUTF8).withPos(`{"a1":{"a2":"b2","c2":`, "/a1/c2"), "/a1/c2"},
643 },
644 wantOut: `{"a1":{"a2":"b2","c2"`,
645 }, {
646 name: jsontest.Name("ErrorPosition/1/a2"),
647 calls: []encoderMethodCall{
648 {Value(` [ "a1" , { "a2" : "b` + "\xff" + `2" } ] `), E(jsonwire.ErrInvalidUTF8).withPos(` [ "a1" , { "a2" : "b`, "/1/a2"), ""},
649 {BeginArray, nil, ""},
650 {String("a1"), nil, "/0"},
651 {Value(` { "a2" : "b` + "\xff" + `2" } `), E(jsonwire.ErrInvalidUTF8).withPos(`["a1", { "a2" : "b`, "/1/a2"), ""},
652 {BeginObject, nil, "/1"},
653 {String("a2"), nil, "/1/a2"},
654 {Value(` "b` + "\xff" + `2" `), E(jsonwire.ErrInvalidUTF8).withPos(`["a1",{"a2": "b`, "/1/a2"), "/1/a2"},
655 {String(`b` + "\xff" + `2`), E(jsonwire.ErrInvalidUTF8).withPos(`["a1",{"a2":`, "/1/a2"), "/1/a2"},
656 },
657 wantOut: `["a1",{"a2"`,
658 }, {
659 name: jsontest.Name("ErrorPosition/c1/1"),
660 calls: []encoderMethodCall{
661 {Value(` { "a1" : "b1" , "c1" : [ "a2" , "b` + "\xff" + `2" ] } `), E(jsonwire.ErrInvalidUTF8).withPos(` { "a1" : "b1" , "c1" : [ "a2" , "b`, "/c1/1"), ""},
662 {BeginObject, nil, ""},
663 {String("a1"), nil, "/a1"},
664 {String("b1"), nil, "/a1"},
665 {String("c1"), nil, "/c1"},
666 {Value(` [ "a2" , "b` + "\xff" + `2" ] `), E(jsonwire.ErrInvalidUTF8).withPos(`{"a1":"b1","c1": [ "a2" , "b`, "/c1/1"), ""},
667 {BeginArray, nil, "/c1"},
668 {String("a2"), nil, "/c1/0"},
669 {Value(` "b` + "\xff" + `2" `), E(jsonwire.ErrInvalidUTF8).withPos(`{"a1":"b1","c1":["a2", "b`, "/c1/1"), "/c1/0"},
670 {String(`b` + "\xff" + `2`), E(jsonwire.ErrInvalidUTF8).withPos(`{"a1":"b1","c1":["a2",`, "/c1/1"), "/c1/0"},
671 },
672 wantOut: `{"a1":"b1","c1":["a2"`,
673 }, {
674 name: jsontest.Name("ErrorPosition/0/a1/1/c3/1"),
675 calls: []encoderMethodCall{
676 {Value(` [ { "a1" : [ "a2" , { "a3" : "b3" , "c3" : [ "a4" , "b` + "\xff" + `4" ] } ] } ] `), E(jsonwire.ErrInvalidUTF8).withPos(` [ { "a1" : [ "a2" , { "a3" : "b3" , "c3" : [ "a4" , "b`, "/0/a1/1/c3/1"), ""},
677 {BeginArray, nil, ""},
678 {Value(` { "a1" : [ "a2" , { "a3" : "b3" , "c3" : [ "a4" , "b` + "\xff" + `4" ] } ] } `), E(jsonwire.ErrInvalidUTF8).withPos(`[ { "a1" : [ "a2" , { "a3" : "b3" , "c3" : [ "a4" , "b`, "/0/a1/1/c3/1"), ""},
679 {BeginObject, nil, "/0"},
680 {String("a1"), nil, "/0/a1"},
681 {Value(` [ "a2" , { "a3" : "b3" , "c3" : [ "a4" , "b` + "\xff" + `4" ] } ] `), E(jsonwire.ErrInvalidUTF8).withPos(`[{"a1": [ "a2" , { "a3" : "b3" , "c3" : [ "a4" , "b`, "/0/a1/1/c3/1"), ""},
682 {BeginArray, nil, ""},
683 {String("a2"), nil, "/0/a1/0"},
684 {Value(` { "a3" : "b3" , "c3" : [ "a4" , "b` + "\xff" + `4" ] } `), E(jsonwire.ErrInvalidUTF8).withPos(`[{"a1":["a2", { "a3" : "b3" , "c3" : [ "a4" , "b`, "/0/a1/1/c3/1"), ""},
685 {BeginObject, nil, "/0/a1/1"},
686 {String("a3"), nil, "/0/a1/1/a3"},
687 {String("b3"), nil, "/0/a1/1/a3"},
688 {String("c3"), nil, "/0/a1/1/c3"},
689 {Value(` [ "a4" , "b` + "\xff" + `4" ] `), E(jsonwire.ErrInvalidUTF8).withPos(`[{"a1":["a2",{"a3":"b3","c3": [ "a4" , "b`, "/0/a1/1/c3/1"), ""},
690 {BeginArray, nil, "/0/a1/1/c3"},
691 {String("a4"), nil, "/0/a1/1/c3/0"},
692 {Value(` "b` + "\xff" + `4" `), E(jsonwire.ErrInvalidUTF8).withPos(`[{"a1":["a2",{"a3":"b3","c3":["a4", "b`, "/0/a1/1/c3/1"), "/0/a1/1/c3/0"},
693 {String(`b` + "\xff" + `4`), E(jsonwire.ErrInvalidUTF8).withPos(`[{"a1":["a2",{"a3":"b3","c3":["a4",`, "/0/a1/1/c3/1"), "/0/a1/1/c3/0"},
694 },
695 wantOut: `[{"a1":["a2",{"a3":"b3","c3":["a4"`,
696 }}
697
698
699
700 func TestEncoderErrors(t *testing.T) {
701 for _, td := range encoderErrorTestdata {
702 t.Run(path.Join(td.name.Name), func(t *testing.T) {
703 testEncoderErrors(t, td.name.Where, td.opts, td.calls, td.wantOut)
704 })
705 }
706 }
707 func testEncoderErrors(t *testing.T, where jsontest.CasePos, opts []Options, calls []encoderMethodCall, wantOut string) {
708 dst := new(bytes.Buffer)
709 enc := NewEncoder(dst, opts...)
710 for i, call := range calls {
711 var gotErr error
712 switch tokVal := call.in.(type) {
713 case Token:
714 gotErr = enc.WriteToken(tokVal)
715 case Value:
716 gotErr = enc.WriteValue(tokVal)
717 }
718 if !equalError(gotErr, call.wantErr) {
719 t.Fatalf("%s: %d: error mismatch:\ngot %v\nwant %v", where, i, gotErr, call.wantErr)
720 }
721 if call.wantPointer != "" {
722 gotPointer := enc.StackPointer()
723 if gotPointer != call.wantPointer {
724 t.Fatalf("%s: %d: Encoder.StackPointer = %s, want %s", where, i, gotPointer, call.wantPointer)
725 }
726 }
727 }
728 gotOut := dst.String() + string(enc.s.unflushedBuffer())
729 if gotOut != wantOut {
730 t.Fatalf("%s: output mismatch:\ngot %q\nwant %q", where, gotOut, wantOut)
731 }
732 gotOffset := int(enc.OutputOffset())
733 wantOffset := len(wantOut)
734 if gotOffset != wantOffset {
735 t.Fatalf("%s: Encoder.OutputOffset = %v, want %v", where, gotOffset, wantOffset)
736 }
737 }
738
View as plain text