1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package zapcore
22
23 import (
24 "encoding/json"
25 "errors"
26 "math"
27 "math/rand"
28 "reflect"
29 "testing"
30 "testing/quick"
31 "time"
32 "unicode/utf8"
33
34 "go.uber.org/zap/buffer"
35 "go.uber.org/zap/internal/bufferpool"
36
37 "github.com/stretchr/testify/assert"
38 "github.com/stretchr/testify/require"
39 "go.uber.org/multierr"
40 )
41
42 var _defaultEncoderConfig = EncoderConfig{
43 EncodeTime: EpochTimeEncoder,
44 EncodeDuration: SecondsDurationEncoder,
45 }
46
47 func TestJSONClone(t *testing.T) {
48
49 parent := &jsonEncoder{buf: bufferpool.Get()}
50 clone := parent.Clone()
51
52
53 parent.AddString("foo", "bar")
54 clone.AddString("baz", "bing")
55
56 assertJSON(t, `"foo":"bar"`, parent)
57 assertJSON(t, `"baz":"bing"`, clone.(*jsonEncoder))
58 }
59
60 func TestJSONEscaping(t *testing.T) {
61 enc := &jsonEncoder{buf: bufferpool.Get()}
62
63 cases := map[string]string{
64
65 `foo`: `foo`,
66
67 `"`: `\"`,
68 `\`: `\\`,
69
70 `foo"foo`: `foo\"foo`,
71 "foo\n": `foo\n`,
72
73 "\n": `\n`,
74 "\r": `\r`,
75 "\t": `\t`,
76
77
78 "\b": `\u0008`,
79 "\f": `\u000c`,
80
81
82
83 "<": "<",
84 ">": ">",
85 "&": "&",
86
87 string(byte(0x07)): `\u0007`,
88
89 `☃`: `☃`,
90
91 "\xed\xa0\x80": `\ufffd\ufffd\ufffd`,
92 "foo\xed\xa0\x80": `foo\ufffd\ufffd\ufffd`,
93 }
94
95 t.Run("String", func(t *testing.T) {
96 for input, output := range cases {
97 enc.truncate()
98 enc.safeAddString(input)
99 assertJSON(t, output, enc)
100 }
101 })
102
103 t.Run("ByteString", func(t *testing.T) {
104 for input, output := range cases {
105 enc.truncate()
106 enc.safeAddByteString([]byte(input))
107 assertJSON(t, output, enc)
108 }
109 })
110 }
111
112 func TestJSONEncoderObjectFields(t *testing.T) {
113 tests := []struct {
114 desc string
115 expected string
116 f func(Encoder)
117 }{
118 {"binary", `"k":"YWIxMg=="`, func(e Encoder) { e.AddBinary("k", []byte("ab12")) }},
119 {"bool", `"k\\":true`, func(e Encoder) { e.AddBool(`k\`, true) }},
120 {"bool", `"k":true`, func(e Encoder) { e.AddBool("k", true) }},
121 {"bool", `"k":false`, func(e Encoder) { e.AddBool("k", false) }},
122 {"byteString", `"k":"v\\"`, func(e Encoder) { e.AddByteString(`k`, []byte(`v\`)) }},
123 {"byteString", `"k":"v"`, func(e Encoder) { e.AddByteString("k", []byte("v")) }},
124 {"byteString", `"k":""`, func(e Encoder) { e.AddByteString("k", []byte{}) }},
125 {"byteString", `"k":""`, func(e Encoder) { e.AddByteString("k", nil) }},
126 {"complex128", `"k":"1+2i"`, func(e Encoder) { e.AddComplex128("k", 1+2i) }},
127 {"complex128/negative_i", `"k":"1-2i"`, func(e Encoder) { e.AddComplex128("k", 1-2i) }},
128 {"complex64", `"k":"1+2i"`, func(e Encoder) { e.AddComplex64("k", 1+2i) }},
129 {"complex64/negative_i", `"k":"1-2i"`, func(e Encoder) { e.AddComplex64("k", 1-2i) }},
130 {"complex64", `"k":"2.71+3.14i"`, func(e Encoder) { e.AddComplex64("k", 2.71+3.14i) }},
131 {"duration", `"k":0.000000001`, func(e Encoder) { e.AddDuration("k", 1) }},
132 {"duration/negative", `"k":-0.000000001`, func(e Encoder) { e.AddDuration("k", -1) }},
133 {"float64", `"k":1`, func(e Encoder) { e.AddFloat64("k", 1.0) }},
134 {"float64", `"k":10000000000`, func(e Encoder) { e.AddFloat64("k", 1e10) }},
135 {"float64", `"k":"NaN"`, func(e Encoder) { e.AddFloat64("k", math.NaN()) }},
136 {"float64", `"k":"+Inf"`, func(e Encoder) { e.AddFloat64("k", math.Inf(1)) }},
137 {"float64", `"k":"-Inf"`, func(e Encoder) { e.AddFloat64("k", math.Inf(-1)) }},
138 {"float64/pi", `"k":3.141592653589793`, func(e Encoder) { e.AddFloat64("k", math.Pi) }},
139 {"float32", `"k":1`, func(e Encoder) { e.AddFloat32("k", 1.0) }},
140 {"float32", `"k":2.71`, func(e Encoder) { e.AddFloat32("k", 2.71) }},
141 {"float32", `"k":0.1`, func(e Encoder) { e.AddFloat32("k", 0.1) }},
142 {"float32", `"k":10000000000`, func(e Encoder) { e.AddFloat32("k", 1e10) }},
143 {"float32", `"k":"NaN"`, func(e Encoder) { e.AddFloat32("k", float32(math.NaN())) }},
144 {"float32", `"k":"+Inf"`, func(e Encoder) { e.AddFloat32("k", float32(math.Inf(1))) }},
145 {"float32", `"k":"-Inf"`, func(e Encoder) { e.AddFloat32("k", float32(math.Inf(-1))) }},
146 {"float32/pi", `"k":3.1415927`, func(e Encoder) { e.AddFloat32("k", math.Pi) }},
147 {"int", `"k":42`, func(e Encoder) { e.AddInt("k", 42) }},
148 {"int64", `"k":42`, func(e Encoder) { e.AddInt64("k", 42) }},
149 {"int64/min", `"k":-9223372036854775808`, func(e Encoder) { e.AddInt64("k", math.MinInt64) }},
150 {"int64/max", `"k":9223372036854775807`, func(e Encoder) { e.AddInt64("k", math.MaxInt64) }},
151 {"int32", `"k":42`, func(e Encoder) { e.AddInt32("k", 42) }},
152 {"int32/min", `"k":-2147483648`, func(e Encoder) { e.AddInt32("k", math.MinInt32) }},
153 {"int32/max", `"k":2147483647`, func(e Encoder) { e.AddInt32("k", math.MaxInt32) }},
154 {"int16", `"k":42`, func(e Encoder) { e.AddInt16("k", 42) }},
155 {"int16/min", `"k":-32768`, func(e Encoder) { e.AddInt16("k", math.MinInt16) }},
156 {"int16/max", `"k":32767`, func(e Encoder) { e.AddInt16("k", math.MaxInt16) }},
157 {"int8", `"k":42`, func(e Encoder) { e.AddInt8("k", 42) }},
158 {"int8/min", `"k":-128`, func(e Encoder) { e.AddInt8("k", math.MinInt8) }},
159 {"int8/max", `"k":127`, func(e Encoder) { e.AddInt8("k", math.MaxInt8) }},
160 {"string", `"k":"v\\"`, func(e Encoder) { e.AddString(`k`, `v\`) }},
161 {"string", `"k":"v"`, func(e Encoder) { e.AddString("k", "v") }},
162 {"string", `"k":""`, func(e Encoder) { e.AddString("k", "") }},
163 {"time", `"k":1`, func(e Encoder) { e.AddTime("k", time.Unix(1, 0)) }},
164 {"uint", `"k":42`, func(e Encoder) { e.AddUint("k", 42) }},
165 {"uint64", `"k":42`, func(e Encoder) { e.AddUint64("k", 42) }},
166 {"uint64/max", `"k":18446744073709551615`, func(e Encoder) { e.AddUint64("k", math.MaxUint64) }},
167 {"uint32", `"k":42`, func(e Encoder) { e.AddUint32("k", 42) }},
168 {"uint32/max", `"k":4294967295`, func(e Encoder) { e.AddUint32("k", math.MaxUint32) }},
169 {"uint16", `"k":42`, func(e Encoder) { e.AddUint16("k", 42) }},
170 {"uint16/max", `"k":65535`, func(e Encoder) { e.AddUint16("k", math.MaxUint16) }},
171 {"uint8", `"k":42`, func(e Encoder) { e.AddUint8("k", 42) }},
172 {"uint8/max", `"k":255`, func(e Encoder) { e.AddUint8("k", math.MaxUint8) }},
173 {"uintptr", `"k":42`, func(e Encoder) { e.AddUintptr("k", 42) }},
174 {
175 desc: "object (success)",
176 expected: `"k":{"loggable":"yes"}`,
177 f: func(e Encoder) {
178 assert.NoError(t, e.AddObject("k", loggable{true}), "Unexpected error calling MarshalLogObject.")
179 },
180 },
181 {
182 desc: "object (error)",
183 expected: `"k":{}`,
184 f: func(e Encoder) {
185 assert.Error(t, e.AddObject("k", loggable{false}), "Expected an error calling MarshalLogObject.")
186 },
187 },
188 {
189 desc: "object (with nested array)",
190 expected: `"turducken":{"ducks":[{"in":"chicken"},{"in":"chicken"}]}`,
191 f: func(e Encoder) {
192 assert.NoError(
193 t,
194 e.AddObject("turducken", turducken{}),
195 "Unexpected error calling MarshalLogObject with nested ObjectMarshalers and ArrayMarshalers.",
196 )
197 },
198 },
199 {
200 desc: "array (with nested object)",
201 expected: `"turduckens":[{"ducks":[{"in":"chicken"},{"in":"chicken"}]},{"ducks":[{"in":"chicken"},{"in":"chicken"}]}]`,
202 f: func(e Encoder) {
203 assert.NoError(
204 t,
205 e.AddArray("turduckens", turduckens(2)),
206 "Unexpected error calling MarshalLogObject with nested ObjectMarshalers and ArrayMarshalers.",
207 )
208 },
209 },
210 {
211 desc: "array (success)",
212 expected: `"k":[true]`,
213 f: func(e Encoder) {
214 assert.NoError(t, e.AddArray(`k`, loggable{true}), "Unexpected error calling MarshalLogArray.")
215 },
216 },
217 {
218 desc: "array (error)",
219 expected: `"k":[]`,
220 f: func(e Encoder) {
221 assert.Error(t, e.AddArray("k", loggable{false}), "Expected an error calling MarshalLogArray.")
222 },
223 },
224 {
225 desc: "reflect (success)",
226 expected: `"k":{"escape":"<&>","loggable":"yes"}`,
227 f: func(e Encoder) {
228 assert.NoError(t, e.AddReflected("k", map[string]string{"escape": "<&>", "loggable": "yes"}), "Unexpected error JSON-serializing a map.")
229 },
230 },
231 {
232 desc: "reflect (failure)",
233 expected: "",
234 f: func(e Encoder) {
235 assert.Error(t, e.AddReflected("k", noJSON{}), "Unexpected success JSON-serializing a noJSON.")
236 },
237 },
238 {
239 desc: "namespace",
240
241 expected: `"outermost":{"outer":{"foo":1,"inner":{"foo":2,"innermost":{`,
242 f: func(e Encoder) {
243 e.OpenNamespace("outermost")
244 e.OpenNamespace("outer")
245 e.AddInt("foo", 1)
246 e.OpenNamespace("inner")
247 e.AddInt("foo", 2)
248 e.OpenNamespace("innermost")
249 },
250 },
251 {
252 desc: "object (no nested namespace)",
253 expected: `"obj":{"obj-out":"obj-outside-namespace"},"not-obj":"should-be-outside-obj"`,
254 f: func(e Encoder) {
255 assert.NoError(t, e.AddObject("obj", maybeNamespace{false}))
256 e.AddString("not-obj", "should-be-outside-obj")
257 },
258 },
259 {
260 desc: "object (with nested namespace)",
261 expected: `"obj":{"obj-out":"obj-outside-namespace","obj-namespace":{"obj-in":"obj-inside-namespace"}},"not-obj":"should-be-outside-obj"`,
262 f: func(e Encoder) {
263 assert.NoError(t, e.AddObject("obj", maybeNamespace{true}))
264 e.AddString("not-obj", "should-be-outside-obj")
265 },
266 },
267 {
268 desc: "multiple open namespaces",
269 expected: `"k":{"foo":1,"middle":{"foo":2,"inner":{"foo":3}}}`,
270 f: func(e Encoder) {
271 err := e.AddObject("k", ObjectMarshalerFunc(func(enc ObjectEncoder) error {
272 e.AddInt("foo", 1)
273 e.OpenNamespace("middle")
274 e.AddInt("foo", 2)
275 e.OpenNamespace("inner")
276 e.AddInt("foo", 3)
277 return nil
278 }))
279 assert.NoError(t, err)
280 },
281 },
282 }
283
284 for _, tt := range tests {
285 t.Run(tt.desc, func(t *testing.T) {
286 assertOutput(t, _defaultEncoderConfig, tt.expected, tt.f)
287 })
288 }
289 }
290
291 func TestJSONEncoderTimeFormats(t *testing.T) {
292 date := time.Date(2000, time.January, 2, 3, 4, 5, 6, time.UTC)
293
294 f := func(e Encoder) {
295 e.AddTime("k", date)
296 err := e.AddArray("a", ArrayMarshalerFunc(func(enc ArrayEncoder) error {
297 enc.AppendTime(date)
298 return nil
299 }))
300 assert.NoError(t, err)
301 }
302 tests := []struct {
303 desc string
304 cfg EncoderConfig
305 expected string
306 }{
307 {
308 desc: "time.Time ISO8601",
309 cfg: EncoderConfig{
310 EncodeDuration: NanosDurationEncoder,
311 EncodeTime: ISO8601TimeEncoder,
312 },
313 expected: `"k":"2000-01-02T03:04:05.000Z","a":["2000-01-02T03:04:05.000Z"]`,
314 },
315 {
316 desc: "time.Time RFC3339",
317 cfg: EncoderConfig{
318 EncodeDuration: NanosDurationEncoder,
319 EncodeTime: RFC3339TimeEncoder,
320 },
321 expected: `"k":"2000-01-02T03:04:05Z","a":["2000-01-02T03:04:05Z"]`,
322 },
323 {
324 desc: "time.Time RFC3339Nano",
325 cfg: EncoderConfig{
326 EncodeDuration: NanosDurationEncoder,
327 EncodeTime: RFC3339NanoTimeEncoder,
328 },
329 expected: `"k":"2000-01-02T03:04:05.000000006Z","a":["2000-01-02T03:04:05.000000006Z"]`,
330 },
331 }
332
333 for _, tt := range tests {
334 t.Run(tt.desc, func(t *testing.T) {
335 assertOutput(t, tt.cfg, tt.expected, f)
336 })
337 }
338 }
339
340 func TestJSONEncoderArrays(t *testing.T) {
341 tests := []struct {
342 desc string
343 expected string
344 f func(ArrayEncoder)
345 }{
346 {"bool", `[true,true]`, func(e ArrayEncoder) { e.AppendBool(true) }},
347 {"byteString", `["k","k"]`, func(e ArrayEncoder) { e.AppendByteString([]byte("k")) }},
348 {"byteString", `["k\\","k\\"]`, func(e ArrayEncoder) { e.AppendByteString([]byte(`k\`)) }},
349 {"complex128", `["1+2i","1+2i"]`, func(e ArrayEncoder) { e.AppendComplex128(1 + 2i) }},
350 {"complex64", `["1+2i","1+2i"]`, func(e ArrayEncoder) { e.AppendComplex64(1 + 2i) }},
351 {"durations", `[0.000000002,0.000000002]`, func(e ArrayEncoder) { e.AppendDuration(2) }},
352 {"float64", `[3.14,3.14]`, func(e ArrayEncoder) { e.AppendFloat64(3.14) }},
353 {"float32", `[3.14,3.14]`, func(e ArrayEncoder) { e.AppendFloat32(3.14) }},
354 {"int", `[42,42]`, func(e ArrayEncoder) { e.AppendInt(42) }},
355 {"int64", `[42,42]`, func(e ArrayEncoder) { e.AppendInt64(42) }},
356 {"int32", `[42,42]`, func(e ArrayEncoder) { e.AppendInt32(42) }},
357 {"int16", `[42,42]`, func(e ArrayEncoder) { e.AppendInt16(42) }},
358 {"int8", `[42,42]`, func(e ArrayEncoder) { e.AppendInt8(42) }},
359 {"string", `["k","k"]`, func(e ArrayEncoder) { e.AppendString("k") }},
360 {"string", `["k\\","k\\"]`, func(e ArrayEncoder) { e.AppendString(`k\`) }},
361 {"times", `[1,1]`, func(e ArrayEncoder) { e.AppendTime(time.Unix(1, 0)) }},
362 {"uint", `[42,42]`, func(e ArrayEncoder) { e.AppendUint(42) }},
363 {"uint64", `[42,42]`, func(e ArrayEncoder) { e.AppendUint64(42) }},
364 {"uint32", `[42,42]`, func(e ArrayEncoder) { e.AppendUint32(42) }},
365 {"uint16", `[42,42]`, func(e ArrayEncoder) { e.AppendUint16(42) }},
366 {"uint8", `[42,42]`, func(e ArrayEncoder) { e.AppendUint8(42) }},
367 {"uintptr", `[42,42]`, func(e ArrayEncoder) { e.AppendUintptr(42) }},
368 {
369 desc: "arrays (success)",
370 expected: `[[true],[true]]`,
371 f: func(arr ArrayEncoder) {
372 assert.NoError(t, arr.AppendArray(ArrayMarshalerFunc(func(inner ArrayEncoder) error {
373 inner.AppendBool(true)
374 return nil
375 })), "Unexpected error appending an array.")
376 },
377 },
378 {
379 desc: "arrays (error)",
380 expected: `[[true],[true]]`,
381 f: func(arr ArrayEncoder) {
382 assert.Error(t, arr.AppendArray(ArrayMarshalerFunc(func(inner ArrayEncoder) error {
383 inner.AppendBool(true)
384 return errors.New("fail")
385 })), "Expected an error appending an array.")
386 },
387 },
388 {
389 desc: "objects (success)",
390 expected: `[{"loggable":"yes"},{"loggable":"yes"}]`,
391 f: func(arr ArrayEncoder) {
392 assert.NoError(t, arr.AppendObject(loggable{true}), "Unexpected error appending an object.")
393 },
394 },
395 {
396 desc: "objects (error)",
397 expected: `[{},{}]`,
398 f: func(arr ArrayEncoder) {
399 assert.Error(t, arr.AppendObject(loggable{false}), "Expected an error appending an object.")
400 },
401 },
402 {
403 desc: "reflect (success)",
404 expected: `[{"foo":5},{"foo":5}]`,
405 f: func(arr ArrayEncoder) {
406 assert.NoError(
407 t,
408 arr.AppendReflected(map[string]int{"foo": 5}),
409 "Unexpected an error appending an object with reflection.",
410 )
411 },
412 },
413 {
414 desc: "reflect (error)",
415 expected: `[]`,
416 f: func(arr ArrayEncoder) {
417 assert.Error(
418 t,
419 arr.AppendReflected(noJSON{}),
420 "Unexpected an error appending an object with reflection.",
421 )
422 },
423 },
424 {
425 desc: "object (no nested namespace) then string",
426 expected: `[{"obj-out":"obj-outside-namespace"},"should-be-outside-obj",{"obj-out":"obj-outside-namespace"},"should-be-outside-obj"]`,
427 f: func(arr ArrayEncoder) {
428 assert.NoError(t, arr.AppendObject(maybeNamespace{false}))
429 arr.AppendString("should-be-outside-obj")
430 },
431 },
432 {
433 desc: "object (with nested namespace) then string",
434 expected: `[{"obj-out":"obj-outside-namespace","obj-namespace":{"obj-in":"obj-inside-namespace"}},"should-be-outside-obj",{"obj-out":"obj-outside-namespace","obj-namespace":{"obj-in":"obj-inside-namespace"}},"should-be-outside-obj"]`,
435 f: func(arr ArrayEncoder) {
436 assert.NoError(t, arr.AppendObject(maybeNamespace{true}))
437 arr.AppendString("should-be-outside-obj")
438 },
439 },
440 }
441
442 for _, tt := range tests {
443 t.Run(tt.desc, func(t *testing.T) {
444 f := func(enc Encoder) error {
445 return enc.AddArray("array", ArrayMarshalerFunc(func(arr ArrayEncoder) error {
446 tt.f(arr)
447 tt.f(arr)
448 return nil
449 }))
450 }
451 assertOutput(t, _defaultEncoderConfig, `"array":`+tt.expected, func(enc Encoder) {
452 err := f(enc)
453 assert.NoError(t, err, "Unexpected error adding array to JSON encoder.")
454 })
455 })
456 }
457 }
458
459 func TestJSONEncoderTimeArrays(t *testing.T) {
460 times := []time.Time{
461 time.Unix(1008720000, 0).UTC(),
462 time.Unix(1040169600, 0).UTC(),
463 time.Unix(1071619200, 0).UTC(),
464 }
465
466 tests := []struct {
467 desc string
468 encoder TimeEncoder
469 want string
470 }{
471 {
472 desc: "epoch",
473 encoder: EpochTimeEncoder,
474 want: `[1008720000,1040169600,1071619200]`,
475 },
476 {
477 desc: "epoch millis",
478 encoder: EpochMillisTimeEncoder,
479 want: `[1008720000000,1040169600000,1071619200000]`,
480 },
481 {
482 desc: "iso8601",
483 encoder: ISO8601TimeEncoder,
484 want: `["2001-12-19T00:00:00.000Z","2002-12-18T00:00:00.000Z","2003-12-17T00:00:00.000Z"]`,
485 },
486 {
487 desc: "rfc3339",
488 encoder: RFC3339TimeEncoder,
489 want: `["2001-12-19T00:00:00Z","2002-12-18T00:00:00Z","2003-12-17T00:00:00Z"]`,
490 },
491 }
492
493 for _, tt := range tests {
494 t.Run(tt.desc, func(t *testing.T) {
495 cfg := _defaultEncoderConfig
496 cfg.EncodeTime = tt.encoder
497
498 enc := &jsonEncoder{buf: bufferpool.Get(), EncoderConfig: &cfg}
499 err := enc.AddArray("array", ArrayMarshalerFunc(func(arr ArrayEncoder) error {
500 for _, time := range times {
501 arr.AppendTime(time)
502 }
503 return nil
504 }))
505 assert.NoError(t, err)
506 assert.Equal(t, `"array":`+tt.want, enc.buf.String())
507 })
508 }
509 }
510
511 func assertJSON(t *testing.T, expected string, enc *jsonEncoder) {
512 assert.Equal(t, expected, enc.buf.String(), "Encoded JSON didn't match expectations.")
513 }
514
515 func assertOutput(t testing.TB, cfg EncoderConfig, expected string, f func(Encoder)) {
516 enc := NewJSONEncoder(cfg).(*jsonEncoder)
517 f(enc)
518 assert.Equal(t, expected, enc.buf.String(), "Unexpected encoder output after adding.")
519
520 enc.truncate()
521 enc.AddString("foo", "bar")
522 f(enc)
523 expectedPrefix := `"foo":"bar"`
524 if expected != "" {
525
526
527 expectedPrefix += ","
528 }
529 assert.Equal(t, expectedPrefix+expected, enc.buf.String(), "Unexpected encoder output after adding as a second field.")
530 }
531
532
533 type turducken struct{}
534
535 func (t turducken) MarshalLogObject(enc ObjectEncoder) error {
536 return enc.AddArray("ducks", ArrayMarshalerFunc(func(arr ArrayEncoder) error {
537 for i := 0; i < 2; i++ {
538 err := arr.AppendObject(ObjectMarshalerFunc(func(inner ObjectEncoder) error {
539 inner.AddString("in", "chicken")
540 return nil
541 }))
542 if err != nil {
543 return err
544 }
545 }
546 return nil
547 }))
548 }
549
550 type turduckens int
551
552 func (t turduckens) MarshalLogArray(enc ArrayEncoder) error {
553 var err error
554 tur := turducken{}
555 for i := 0; i < int(t); i++ {
556 err = multierr.Append(err, enc.AppendObject(tur))
557 }
558 return err
559 }
560
561 type loggable struct{ bool }
562
563 func (l loggable) MarshalLogObject(enc ObjectEncoder) error {
564 if !l.bool {
565 return errors.New("can't marshal")
566 }
567 enc.AddString("loggable", "yes")
568 return nil
569 }
570
571 func (l loggable) MarshalLogArray(enc ArrayEncoder) error {
572 if !l.bool {
573 return errors.New("can't marshal")
574 }
575 enc.AppendBool(true)
576 return nil
577 }
578
579
580 type maybeNamespace struct{ bool }
581
582 func (m maybeNamespace) MarshalLogObject(enc ObjectEncoder) error {
583 enc.AddString("obj-out", "obj-outside-namespace")
584 if m.bool {
585 enc.OpenNamespace("obj-namespace")
586 enc.AddString("obj-in", "obj-inside-namespace")
587 }
588 return nil
589 }
590
591 type noJSON struct{}
592
593 func (nj noJSON) MarshalJSON() ([]byte, error) {
594 return nil, errors.New("no")
595 }
596
597 func zapEncode(encode func(*jsonEncoder, string)) func(s string) []byte {
598 return func(s string) []byte {
599 enc := &jsonEncoder{buf: bufferpool.Get()}
600
601 var ret []byte
602 encode(enc, s)
603 ret = make([]byte, 0, enc.buf.Len()+2)
604 ret = append(ret, '"')
605 ret = append(ret, enc.buf.Bytes()...)
606 ret = append(ret, '"')
607 return ret
608 }
609 }
610
611 func roundTripsCorrectly(encode func(string) []byte, original string) bool {
612
613
614 encoded := encode(original)
615
616 var decoded string
617 err := json.Unmarshal(encoded, &decoded)
618 if err != nil {
619 return false
620 }
621 return original == decoded
622 }
623
624 func roundTripsCorrectlyString(original string) bool {
625 return roundTripsCorrectly(zapEncode((*jsonEncoder).safeAddString), original)
626 }
627
628 func roundTripsCorrectlyByteString(original string) bool {
629 return roundTripsCorrectly(
630 zapEncode(func(enc *jsonEncoder, s string) {
631 enc.safeAddByteString([]byte(s))
632 }),
633 original)
634 }
635
636 type ASCII string
637
638 func (s ASCII) Generate(r *rand.Rand, size int) reflect.Value {
639 bs := make([]byte, size)
640 for i := range bs {
641 bs[i] = byte(r.Intn(128))
642 }
643 a := ASCII(bs)
644 return reflect.ValueOf(a)
645 }
646
647 func asciiRoundTripsCorrectlyString(s ASCII) bool {
648 return roundTripsCorrectlyString(string(s))
649 }
650
651 func asciiRoundTripsCorrectlyByteString(s ASCII) bool {
652 return roundTripsCorrectlyByteString(string(s))
653 }
654
655 func TestJSONQuick(t *testing.T) {
656 check := func(f interface{}) {
657 err := quick.Check(f, &quick.Config{MaxCountScale: 100.0})
658 assert.NoError(t, err)
659 }
660
661 check(roundTripsCorrectlyString)
662 check(roundTripsCorrectlyByteString)
663
664
665 check(asciiRoundTripsCorrectlyString)
666 check(asciiRoundTripsCorrectlyByteString)
667 }
668
669 var _stringLikeCorpus = []string{
670 "",
671 "foo",
672 "bar",
673 "a\nb",
674 "a\tb",
675 "a\\b",
676 `a"b`,
677 }
678
679 func FuzzSafeAppendStringLike_bytes(f *testing.F) {
680 for _, s := range _stringLikeCorpus {
681 f.Add([]byte(s))
682 }
683 f.Fuzz(func(t *testing.T, b []byte) {
684 if !utf8.Valid(b) {
685 t.Skip()
686 }
687
688 fuzzSafeAppendStringLike(t, string(b), func(buf *buffer.Buffer) {
689 safeAppendStringLike(
690 (*buffer.Buffer).AppendBytes,
691 utf8.DecodeRune,
692 buf,
693 b,
694 )
695 })
696 })
697 }
698
699 func FuzzSafeAppendStringLike_string(f *testing.F) {
700 for _, s := range _stringLikeCorpus {
701 f.Add(s)
702 }
703 f.Fuzz(func(t *testing.T, s string) {
704 if !utf8.ValidString(s) {
705 t.Skip()
706 }
707
708 fuzzSafeAppendStringLike(t, s, func(buf *buffer.Buffer) {
709 safeAppendStringLike(
710 (*buffer.Buffer).AppendString,
711 utf8.DecodeRuneInString,
712 buf,
713 s,
714 )
715 })
716 })
717 }
718
719 func fuzzSafeAppendStringLike(
720 t *testing.T,
721 want string,
722 writeString func(*buffer.Buffer),
723 ) {
724 t.Helper()
725
726 buf := bufferpool.Get()
727 defer buf.Free()
728
729 buf.AppendByte('"')
730 writeString(buf)
731 buf.AppendByte('"')
732
733 var got string
734 require.NoError(t, json.Unmarshal(buf.Bytes(), &got))
735 assert.Equal(t, want, got)
736 }
737
View as plain text