mirror of
https://github.com/rs/zerolog
synced 2026-06-08 17:13:30 +00:00
8148645974
* Add array handling of IPAddr and IPPrefix Updated documentation, benchmark, and tests including catching up some missing. Cleaned up couple lint messages * Added cbor encoding for []net.IP and []net.IPNet * Fix binary_test Typos galore, thanks CI * Remove feral x character * Added IPAddrs and IPPrefixes tests for json encoder * Increase code coverage and test cleanup Added Type to the BenchmarkLogFieldType and BenchmarkContexdtFieldType test cases, also to log_test.go Moved the test-fixture data for them and also shared them with new event test for nil events. Added a couple strings for encodeByteTest for FF, CR, LF Added complete cbor testing for AppendInt*, AppendInts*, AppendUint*, and AppendUints* methods. Added test for float IsNaN and Inf(0) and Inf(-1). Added all min/max/smallest value tests for ints and floats Extended the Array test to include IPPrefix, MACAddr, Interface, and object marshaled. Better test of MarshalZerologObject. Added test for encoder AppendStrings and AppendStringers. * Remove merge detritus. * Remove code duplication when appending objects * Add missing Stringers support to Context * Add test cases for Err and Object in Array * Add missing benchmark for Events and Contexts Events: Any, Bytes, Hex, Float32, Floats32, Stringer, Stringers, Timestamp Context: Any, Bytes, Hex, Float32, Floats32, Stringers (also renamed Float,Floats to Float64,Floats64) * Add test of MarshalZeroLogObject of error * Build out Event test cases Test of nil Event - write - Array, Dict - Fields - Int8, Ints8, Int16, Ints16, Int32, Ints32, Int64, Ints64 - Uint8, Uints8, Uint16, Uints16, Uint32, Uints32, Uint64, Uints64 - RawCBOR, RawJSON, EmbedObject - Stringers - Caller, CallerSkip, Stack - Send, Msg, Msgf, MsgFunc - (renamed tests of Float, Floats to Float64, Floats64) Added test of MsgFunc * Add more tests - Dur (With) - Any (With, Fields, Fields map, Disabled) - Interface - Stack (Fields) Fix issue with testing a nil loggableError * Split out the With tests again type arrays Simplifies the "want" maintenance * Add missing test fixture data Bytes, Floats32, Ints8, Ints16, Ints32, Ints64, Uints8, Uints16, Uints32, Uints64, RawJSON, RawCBOR Stringers is now an array (not a single Stringer) (also renamed Float, Floats to Float64, Floats64) * Add examples for IPAddrs, IPPrefixes, Times Missing examples that increase coverage * Build out the complete ErrorMarshalFunc tests * Add AppendStrings to CBOR tests * Add CBOR AppendTimes, AppendDuration, AppendDurations tests * Add CBOR tests - BeginMarker, EndMarker, AppendArrayDim, AppendLineBreak - AppendObjectData - AppendIterface - AppendType - AppendHex - Empty arrays for - Bools - Floats32, Floats64 - Large arrays for - Bools, - Uints8, Uints16, Uints32, Uints64 - Floats32, Floats64 - IPAddrs, IPPrefixes - Small arrays for - Ints, Ints8, Ints16, Ints32, Ints64 - Uints, Uints32, Uints64 - Floats32, Floats64 * Add CBOR test for AppendKey * Added more string encoding tests Test escape character handling. * Fix test import cycle * Rename hex to hexCharacters to avoid namespace. * Remove leftover comment * Add test cases to share between CBOR and JSON tests * Switched CBOR to shared test cases * Add JSON test for AppendKey * Switch to shared test cases Added test for AppendStringer * Added JSON test - AppendNil - AppendBeginMarker, AppendEndMarker - AppendArrayStart, AppendArrayEnd - AppendArrayDelim - AppendLineBreak - AppendObjectData - AppendInterface - AppendBool, AppendBools * Add JSON time tests - AppendTime, AppendTimes - AppendTime (past/present) (integer/float) - AppendDuration, AppendDurations * Added JSON tests for plurals Split out the Int and Float tests into distinct files. Add - AppendInt - AppendInts8, AppendInts16, AppendInts32, AppendInts64, AppendInts - AppendUints8, AppendUints16, AppendUints32, AppendUints64, AppendUints - AppendFloats32, AppendFloats64
434 lines
8.4 KiB
Go
434 lines
8.4 KiB
Go
package json
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"math"
|
|
"math/rand"
|
|
"testing"
|
|
|
|
"github.com/rs/zerolog/internal"
|
|
)
|
|
|
|
var float64Tests = []struct {
|
|
Name string
|
|
Val float64
|
|
Want string
|
|
}{
|
|
{
|
|
Name: "Positive integer",
|
|
Val: 1234.0,
|
|
Want: "1234",
|
|
},
|
|
{
|
|
Name: "Negative integer",
|
|
Val: -5678.0,
|
|
Want: "-5678",
|
|
},
|
|
{
|
|
Name: "Positive decimal",
|
|
Val: 12.3456,
|
|
Want: "12.3456",
|
|
},
|
|
{
|
|
Name: "Negative decimal",
|
|
Val: -78.9012,
|
|
Want: "-78.9012",
|
|
},
|
|
{
|
|
Name: "Large positive number",
|
|
Val: 123456789.0,
|
|
Want: "123456789",
|
|
},
|
|
{
|
|
Name: "Large negative number",
|
|
Val: -987654321.0,
|
|
Want: "-987654321",
|
|
},
|
|
{
|
|
Name: "Zero",
|
|
Val: 0.0,
|
|
Want: "0",
|
|
},
|
|
{
|
|
Name: "Smallest positive value",
|
|
Val: math.SmallestNonzeroFloat64,
|
|
Want: "5e-324",
|
|
},
|
|
{
|
|
Name: "Largest positive value",
|
|
Val: math.MaxFloat64,
|
|
Want: "1.7976931348623157e+308",
|
|
},
|
|
{
|
|
Name: "Smallest negative value",
|
|
Val: -math.SmallestNonzeroFloat64,
|
|
Want: "-5e-324",
|
|
},
|
|
{
|
|
Name: "Largest negative value",
|
|
Val: -math.MaxFloat64,
|
|
Want: "-1.7976931348623157e+308",
|
|
},
|
|
{
|
|
Name: "NaN",
|
|
Val: math.NaN(),
|
|
Want: `"NaN"`,
|
|
},
|
|
{
|
|
Name: "+Inf",
|
|
Val: math.Inf(1),
|
|
Want: `"+Inf"`,
|
|
},
|
|
{
|
|
Name: "-Inf",
|
|
Val: math.Inf(-1),
|
|
Want: `"-Inf"`,
|
|
},
|
|
{
|
|
Name: "Clean up e-09 to e-9 case 1",
|
|
Val: 1e-9,
|
|
Want: "1e-9",
|
|
},
|
|
{
|
|
Name: "Clean up e-09 to e-9 case 2",
|
|
Val: -2.236734e-9,
|
|
Want: "-2.236734e-9",
|
|
},
|
|
}
|
|
|
|
func TestEncoder_AppendFloat64(t *testing.T) {
|
|
for _, tc := range float64Tests {
|
|
t.Run(tc.Name, func(t *testing.T) {
|
|
var b []byte
|
|
b = (Encoder{}).AppendFloat64(b, tc.Val, -1)
|
|
if s := string(b); tc.Want != s {
|
|
t.Errorf("%q", s)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func FuzzEncoder_AppendFloat64(f *testing.F) {
|
|
for _, tc := range float64Tests {
|
|
f.Add(tc.Val)
|
|
}
|
|
f.Fuzz(func(t *testing.T, val float64) {
|
|
actual := (Encoder{}).AppendFloat64(nil, val, -1)
|
|
if len(actual) == 0 {
|
|
t.Fatal("empty buffer")
|
|
}
|
|
|
|
if actual[0] == '"' {
|
|
switch string(actual) {
|
|
case `"NaN"`:
|
|
if !math.IsNaN(val) {
|
|
t.Fatalf("expected %v got NaN", val)
|
|
}
|
|
case `"+Inf"`:
|
|
if !math.IsInf(val, 1) {
|
|
t.Fatalf("expected %v got +Inf", val)
|
|
}
|
|
case `"-Inf"`:
|
|
if !math.IsInf(val, -1) {
|
|
t.Fatalf("expected %v got -Inf", val)
|
|
}
|
|
default:
|
|
t.Fatalf("unexpected string: %s", actual)
|
|
}
|
|
return
|
|
}
|
|
|
|
if expected, err := json.Marshal(val); err != nil {
|
|
t.Error(err)
|
|
} else if string(actual) != string(expected) {
|
|
t.Errorf("expected %s, got %s", expected, actual)
|
|
}
|
|
|
|
var parsed float64
|
|
if err := json.Unmarshal(actual, &parsed); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if parsed != val {
|
|
t.Fatalf("expected %v, got %v", val, parsed)
|
|
}
|
|
})
|
|
}
|
|
|
|
var float32Tests = []struct {
|
|
Name string
|
|
Val float32
|
|
Want string
|
|
}{
|
|
{
|
|
Name: "Positive integer",
|
|
Val: 1234.0,
|
|
Want: "1234",
|
|
},
|
|
{
|
|
Name: "Negative integer",
|
|
Val: -5678.0,
|
|
Want: "-5678",
|
|
},
|
|
{
|
|
Name: "Positive decimal",
|
|
Val: 12.3456,
|
|
Want: "12.3456",
|
|
},
|
|
{
|
|
Name: "Negative decimal",
|
|
Val: -78.9012,
|
|
Want: "-78.9012",
|
|
},
|
|
{
|
|
Name: "Large positive number",
|
|
Val: 123456789.0,
|
|
Want: "123456790",
|
|
},
|
|
{
|
|
Name: "Large negative number",
|
|
Val: -987654321.0,
|
|
Want: "-987654340",
|
|
},
|
|
{
|
|
Name: "Zero",
|
|
Val: 0.0,
|
|
Want: "0",
|
|
},
|
|
{
|
|
Name: "Smallest positive value",
|
|
Val: math.SmallestNonzeroFloat32,
|
|
Want: "1e-45",
|
|
},
|
|
{
|
|
Name: "Largest positive value",
|
|
Val: math.MaxFloat32,
|
|
Want: "3.4028235e+38",
|
|
},
|
|
{
|
|
Name: "Smallest negative value",
|
|
Val: -math.SmallestNonzeroFloat32,
|
|
Want: "-1e-45",
|
|
},
|
|
{
|
|
Name: "Largest negative value",
|
|
Val: -math.MaxFloat32,
|
|
Want: "-3.4028235e+38",
|
|
},
|
|
{
|
|
Name: "NaN",
|
|
Val: float32(math.NaN()),
|
|
Want: `"NaN"`,
|
|
},
|
|
{
|
|
Name: "+Inf",
|
|
Val: float32(math.Inf(1)),
|
|
Want: `"+Inf"`,
|
|
},
|
|
{
|
|
Name: "-Inf",
|
|
Val: float32(math.Inf(-1)),
|
|
Want: `"-Inf"`,
|
|
},
|
|
{
|
|
Name: "Clean up e-09 to e-9 case 1",
|
|
Val: 1e-9,
|
|
Want: "1e-9",
|
|
},
|
|
{
|
|
Name: "Clean up e-09 to e-9 case 2",
|
|
Val: -2.236734e-9,
|
|
Want: "-2.236734e-9",
|
|
},
|
|
}
|
|
|
|
func TestEncoder_AppendFloat32(t *testing.T) {
|
|
for _, tc := range float32Tests {
|
|
t.Run(tc.Name, func(t *testing.T) {
|
|
var b []byte
|
|
b = (Encoder{}).AppendFloat32(b, tc.Val, -1)
|
|
if s := string(b); tc.Want != s {
|
|
t.Errorf("%q", s)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func FuzzEncoder_AppendFloat32(f *testing.F) {
|
|
for _, tc := range float32Tests {
|
|
f.Add(tc.Val)
|
|
}
|
|
f.Fuzz(func(t *testing.T, val float32) {
|
|
actual := (Encoder{}).AppendFloat32(nil, val, -1)
|
|
if len(actual) == 0 {
|
|
t.Fatal("empty buffer")
|
|
}
|
|
|
|
if actual[0] == '"' {
|
|
val := float64(val)
|
|
switch string(actual) {
|
|
case `"NaN"`:
|
|
if !math.IsNaN(val) {
|
|
t.Fatalf("expected %v got NaN", val)
|
|
}
|
|
case `"+Inf"`:
|
|
if !math.IsInf(val, 1) {
|
|
t.Fatalf("expected %v got +Inf", val)
|
|
}
|
|
case `"-Inf"`:
|
|
if !math.IsInf(val, -1) {
|
|
t.Fatalf("expected %v got -Inf", val)
|
|
}
|
|
default:
|
|
t.Fatalf("unexpected string: %s", actual)
|
|
}
|
|
return
|
|
}
|
|
|
|
if expected, err := json.Marshal(val); err != nil {
|
|
t.Error(err)
|
|
} else if string(actual) != string(expected) {
|
|
t.Errorf("expected %s, got %s", expected, actual)
|
|
}
|
|
|
|
var parsed float32
|
|
if err := json.Unmarshal(actual, &parsed); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if parsed != val {
|
|
t.Fatalf("expected %v, got %v", val, parsed)
|
|
}
|
|
})
|
|
}
|
|
|
|
func generateFloat32s(n int) []float32 {
|
|
floats := make([]float32, n)
|
|
for i := 0; i < n; i++ {
|
|
floats[i] = rand.Float32()
|
|
}
|
|
return floats
|
|
}
|
|
|
|
func generateFloat64s(n int) []float64 {
|
|
floats := make([]float64, n)
|
|
for i := 0; i < n; i++ {
|
|
floats[i] = rand.Float64()
|
|
}
|
|
return floats
|
|
}
|
|
|
|
func TestAppendFloats32(t *testing.T) {
|
|
doOne := func(vals []float32) {
|
|
want := make([]byte, 0)
|
|
want = append(want, '[')
|
|
for i, val := range vals {
|
|
if math.IsNaN(float64(val)) {
|
|
want = append(want, []byte(`"NaN"`)...)
|
|
} else if math.IsInf(float64(val), 1) {
|
|
want = append(want, []byte(`"+Inf"`)...)
|
|
} else if math.IsInf(float64(val), -1) {
|
|
want = append(want, []byte(`"-Inf"`)...)
|
|
} else {
|
|
want = append(want, []byte(fmt.Sprintf("%v", float32(val)))...)
|
|
}
|
|
if i < len(vals)-1 {
|
|
want = append(want, ',')
|
|
}
|
|
}
|
|
want = append(want, ']')
|
|
|
|
got := enc.AppendFloats32([]byte{}, vals, -1)
|
|
if !bytes.Equal(got, want) {
|
|
t.Errorf("AppendFloats32(%v)\ngot: %s\nwant: %s",
|
|
vals,
|
|
string(got),
|
|
string(want))
|
|
}
|
|
}
|
|
|
|
array := make([]float32, 0)
|
|
for _, tc := range internal.Float32TestCases {
|
|
if tc.Val > 0 && tc.Val < 1e-4 {
|
|
continue // we want to ignore very small numbers for this test
|
|
}
|
|
array = append(array, float32(tc.Val))
|
|
}
|
|
|
|
doOne(array)
|
|
doOne(array[:1]) // single element
|
|
doOne(array[:0]) // edge case of zero length
|
|
}
|
|
|
|
func TestAppendFloats64(t *testing.T) {
|
|
doOne := func(vals []float64) {
|
|
want := make([]byte, 0)
|
|
want = append(want, '[')
|
|
for i, val := range vals {
|
|
if math.IsNaN(val) {
|
|
want = append(want, []byte(`"NaN"`)...)
|
|
} else if math.IsInf(val, 1) {
|
|
want = append(want, []byte(`"+Inf"`)...)
|
|
} else if math.IsInf(val, -1) {
|
|
want = append(want, []byte(`"-Inf"`)...)
|
|
} else {
|
|
want = append(want, []byte(fmt.Sprintf("%v", val))...)
|
|
}
|
|
if i < len(vals)-1 {
|
|
want = append(want, ',')
|
|
}
|
|
}
|
|
want = append(want, ']')
|
|
|
|
got := enc.AppendFloats64([]byte{}, vals, -1)
|
|
if !bytes.Equal(got, want) {
|
|
t.Errorf("AppendFloats64(%v)\ngot: %s\nwant: %s",
|
|
vals,
|
|
string(got),
|
|
string(want))
|
|
}
|
|
}
|
|
|
|
array := make([]float64, 0)
|
|
for _, tc := range internal.Float64TestCases {
|
|
if tc.Val > 0 && tc.Val < 1e-4 {
|
|
continue // we want to ignore very small numbers for this test
|
|
}
|
|
array = append(array, tc.Val)
|
|
}
|
|
|
|
doOne(array)
|
|
doOne(array[:1]) // single element
|
|
doOne(array[:0]) // edge case of zero length
|
|
}
|
|
|
|
// this is really just for the memory allocation characteristics
|
|
func BenchmarkEncoder_AppendFloat32(b *testing.B) {
|
|
floats := append(generateFloat32s(5000), float32(math.NaN()), float32(math.Inf(1)), float32(math.Inf(-1)))
|
|
dst := make([]byte, 0, 128)
|
|
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
for _, f := range floats {
|
|
dst = (Encoder{}).AppendFloat32(dst[:0], f, -1)
|
|
}
|
|
}
|
|
}
|
|
|
|
// this is really just for the memory allocation characteristics
|
|
func BenchmarkEncoder_AppendFloat64(b *testing.B) {
|
|
floats := append(generateFloat64s(5000), math.NaN(), math.Inf(1), math.Inf(-1))
|
|
dst := make([]byte, 0, 128)
|
|
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
for _, f := range floats {
|
|
dst = (Encoder{}).AppendFloat64(dst[:0], f, -1)
|
|
}
|
|
}
|
|
}
|