mirror of
https://github.com/rs/zerolog
synced 2026-06-08 17:13:30 +00:00
Chore improve test coverage (#735)
* 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
This commit is contained in:
@@ -23,3 +23,5 @@ _testmain.go
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
|
||||
coverage.out
|
||||
|
||||
@@ -59,11 +59,7 @@ func (a *Array) write(dst []byte) []byte {
|
||||
// Object marshals an object that implement the LogObjectMarshaler
|
||||
// interface and appends it to the array.
|
||||
func (a *Array) Object(obj LogObjectMarshaler) *Array {
|
||||
e := Dict()
|
||||
obj.MarshalZerologObject(e)
|
||||
e.buf = enc.AppendEndMarker(e.buf)
|
||||
a.buf = append(enc.AppendArrayDelim(a.buf), e.buf...)
|
||||
putEvent(e)
|
||||
a.buf = appendObject(enc.AppendArrayDelim(a.buf), obj)
|
||||
return a
|
||||
}
|
||||
|
||||
@@ -95,11 +91,7 @@ func (a *Array) RawJSON(val []byte) *Array {
|
||||
func (a *Array) Err(err error) *Array {
|
||||
switch m := ErrorMarshalFunc(err).(type) {
|
||||
case LogObjectMarshaler:
|
||||
e := newEvent(nil, 0)
|
||||
e.buf = e.buf[:0]
|
||||
e.appendObject(m)
|
||||
a.buf = append(enc.AppendArrayDelim(a.buf), e.buf...)
|
||||
putEvent(e)
|
||||
a = a.Object(m)
|
||||
case error:
|
||||
if m == nil || isNilValue(m) {
|
||||
a.buf = enc.AppendNil(enc.AppendArrayDelim(a.buf))
|
||||
|
||||
+22
-2
@@ -1,6 +1,7 @@
|
||||
package zerolog
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -27,12 +28,31 @@ func TestArray(t *testing.T) {
|
||||
RawJSON([]byte(`{"some":"json"}`)).
|
||||
Time(time.Time{}).
|
||||
IPAddr(net.IP{192, 168, 0, 10}).
|
||||
IPPrefix(net.IPNet{IP: net.IP{127, 0, 0, 0}, Mask: net.CIDRMask(24, 32)}).
|
||||
MACAddr(net.HardwareAddr{0x01, 0x23, 0x45, 0x67, 0x89, 0xab}).
|
||||
Interface(struct {
|
||||
Pub string
|
||||
Tag string `json:"tag"`
|
||||
priv int
|
||||
}{"A", "j", -5}).
|
||||
Interface(logObjectMarshalerImpl{
|
||||
name: "ZOT",
|
||||
age: 35,
|
||||
}).
|
||||
Dur(0).
|
||||
Dict(Dict().
|
||||
Str("bar", "baz").
|
||||
Int("n", 1),
|
||||
)
|
||||
want := `[true,1,2,3,4,5,6,7,8,9,10,11.98122,12.987654321,"a","b","1f",{"some":"json"},"0001-01-01T00:00:00Z","192.168.0.10",0,{"bar":"baz","n":1}]`
|
||||
).
|
||||
Err(nil).
|
||||
Err(fmt.Errorf("failure")).
|
||||
Err(errorObjectMarshalerImpl{fmt.Errorf("oops")}).
|
||||
Object(logObjectMarshalerImpl{
|
||||
name: "ZIT",
|
||||
age: 22,
|
||||
})
|
||||
|
||||
want := `[true,1,2,3,4,5,6,7,8,9,10,11.98122,12.987654321,"a","b","1f",{"some":"json"},"0001-01-01T00:00:00Z","192.168.0.10","127.0.0.0/24","01:23:45:67:89:ab",{"Pub":"A","tag":"j"},{"name":"zot","age":-35},0,{"bar":"baz","n":1},null,"failure",{"error":"OOPS"},{"name":"zit","age":-22}]`
|
||||
if got := decodeObjectToStr(a.write([]byte{})); got != want {
|
||||
t.Errorf("Array.write()\ngot: %s\nwant: %s", got, want)
|
||||
}
|
||||
|
||||
+110
-183
@@ -1,10 +1,8 @@
|
||||
package zerolog
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
@@ -86,22 +84,10 @@ func BenchmarkLogFields(b *testing.B) {
|
||||
})
|
||||
}
|
||||
|
||||
type obj struct {
|
||||
Pub string
|
||||
Tag string `json:"tag"`
|
||||
priv int
|
||||
}
|
||||
|
||||
func (o obj) MarshalZerologObject(e *Event) {
|
||||
e.Str("Pub", o.Pub).
|
||||
Str("Tag", o.Tag).
|
||||
Int("priv", o.priv)
|
||||
}
|
||||
|
||||
func BenchmarkLogArrayObject(b *testing.B) {
|
||||
obj1 := obj{"a", "b", 2}
|
||||
obj2 := obj{"c", "d", 3}
|
||||
obj3 := obj{"e", "f", 4}
|
||||
obj1 := fixtureObj{"a", "b", 2}
|
||||
obj2 := fixtureObj{"c", "d", 3}
|
||||
obj3 := fixtureObj{"e", "f", 4}
|
||||
logger := New(io.Discard)
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
@@ -115,138 +101,112 @@ func BenchmarkLogArrayObject(b *testing.B) {
|
||||
}
|
||||
|
||||
func BenchmarkLogFieldType(b *testing.B) {
|
||||
bools := []bool{true, false, true, false, true, false, true, false, true, false}
|
||||
ints := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
|
||||
floats := []float64{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
|
||||
strings := []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"}
|
||||
durations := []time.Duration{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
|
||||
times := []time.Time{
|
||||
time.Unix(0, 0),
|
||||
time.Unix(1, 0),
|
||||
time.Unix(2, 0),
|
||||
time.Unix(3, 0),
|
||||
time.Unix(4, 0),
|
||||
time.Unix(5, 0),
|
||||
time.Unix(6, 0),
|
||||
time.Unix(7, 0),
|
||||
time.Unix(8, 0),
|
||||
time.Unix(9, 0),
|
||||
}
|
||||
interfaces := []struct {
|
||||
Pub string
|
||||
Tag string `json:"tag"`
|
||||
priv int
|
||||
}{
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
}
|
||||
objects := []obj{
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
}
|
||||
ipAddrV4 := net.IP{192, 168, 0, 1}
|
||||
ipAddrV6 := net.IP{0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x34}
|
||||
ipAddrs := []net.IP{ipAddrV4, ipAddrV6, ipAddrV4, ipAddrV6, ipAddrV4, ipAddrV6, ipAddrV4, ipAddrV6, ipAddrV4, ipAddrV6}
|
||||
ipPfxV4 := net.IPNet{IP: net.IP{192, 168, 0, 0}, Mask: net.CIDRMask(24, 32)}
|
||||
ipPfxV6 := net.IPNet{IP: net.IP{0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x00}, Mask: net.CIDRMask(64, 128)}
|
||||
ipPfxs := []net.IPNet{ipPfxV4, ipPfxV6, ipPfxV4, ipPfxV6, ipPfxV4, ipPfxV6, ipPfxV4, ipPfxV6, ipPfxV4, ipPfxV6}
|
||||
macAddr := net.HardwareAddr{0x00, 0x1A, 0x2B, 0x3C, 0x4D, 0x5E}
|
||||
|
||||
errs := []error{errors.New("a"), errors.New("b"), errors.New("c"), errors.New("d"), errors.New("e")}
|
||||
ctx := context.Background()
|
||||
fixtures := makeFieldFixtures()
|
||||
types := map[string]func(e *Event) *Event{
|
||||
"Any": func(e *Event) *Event {
|
||||
return e.Any("k", fixtures.Interfaces[0])
|
||||
},
|
||||
"Bool": func(e *Event) *Event {
|
||||
return e.Bool("k", bools[0])
|
||||
return e.Bool("k", fixtures.Bools[0])
|
||||
},
|
||||
"Bools": func(e *Event) *Event {
|
||||
return e.Bools("k", bools)
|
||||
return e.Bools("k", fixtures.Bools)
|
||||
},
|
||||
"Bytes": func(e *Event) *Event {
|
||||
return e.Bytes("k", fixtures.Bytes)
|
||||
},
|
||||
"Hex": func(e *Event) *Event {
|
||||
return e.Hex("k", fixtures.Bytes)
|
||||
},
|
||||
"Int": func(e *Event) *Event {
|
||||
return e.Int("k", ints[0])
|
||||
return e.Int("k", fixtures.Ints[0])
|
||||
},
|
||||
"Ints": func(e *Event) *Event {
|
||||
return e.Ints("k", ints)
|
||||
return e.Ints("k", fixtures.Ints)
|
||||
},
|
||||
"Float": func(e *Event) *Event {
|
||||
return e.Float64("k", floats[0])
|
||||
"Float32": func(e *Event) *Event {
|
||||
return e.Float32("k", fixtures.Floats32[0])
|
||||
},
|
||||
"Floats": func(e *Event) *Event {
|
||||
return e.Floats64("k", floats)
|
||||
"Floats32": func(e *Event) *Event {
|
||||
return e.Floats32("k", fixtures.Floats32)
|
||||
},
|
||||
"Float64": func(e *Event) *Event {
|
||||
return e.Float64("k", fixtures.Floats64[0])
|
||||
},
|
||||
"Floats64": func(e *Event) *Event {
|
||||
return e.Floats64("k", fixtures.Floats64)
|
||||
},
|
||||
"Str": func(e *Event) *Event {
|
||||
return e.Str("k", strings[0])
|
||||
return e.Str("k", fixtures.Strings[0])
|
||||
},
|
||||
"Strs": func(e *Event) *Event {
|
||||
return e.Strs("k", strings)
|
||||
return e.Strs("k", fixtures.Strings)
|
||||
},
|
||||
"Stringer": func(e *Event) *Event {
|
||||
return e.Stringer("k", fixtures.Stringers[0])
|
||||
},
|
||||
"Stringers": func(e *Event) *Event {
|
||||
return e.Stringers("k", fixtures.Stringers)
|
||||
},
|
||||
"Err": func(e *Event) *Event {
|
||||
return e.Err(errs[0])
|
||||
return e.Err(fixtures.Errs[0])
|
||||
},
|
||||
"Errs": func(e *Event) *Event {
|
||||
return e.Errs("k", errs)
|
||||
return e.Errs("k", fixtures.Errs)
|
||||
},
|
||||
"Ctx": func(e *Event) *Event {
|
||||
return e.Ctx(ctx)
|
||||
return e.Ctx(fixtures.Ctx)
|
||||
},
|
||||
"Time": func(e *Event) *Event {
|
||||
return e.Time("k", times[0])
|
||||
return e.Time("k", fixtures.Times[0])
|
||||
},
|
||||
"Times": func(e *Event) *Event {
|
||||
return e.Times("k", times)
|
||||
return e.Times("k", fixtures.Times)
|
||||
},
|
||||
"Dur": func(e *Event) *Event {
|
||||
return e.Dur("k", durations[0])
|
||||
return e.Dur("k", fixtures.Durations[0])
|
||||
},
|
||||
"Durs": func(e *Event) *Event {
|
||||
return e.Durs("k", durations)
|
||||
return e.Durs("k", fixtures.Durations)
|
||||
},
|
||||
"Interface": func(e *Event) *Event {
|
||||
return e.Interface("k", interfaces[0])
|
||||
return e.Interface("k", fixtures.Interfaces[0])
|
||||
},
|
||||
"Interfaces": func(e *Event) *Event {
|
||||
return e.Interface("k", interfaces)
|
||||
return e.Interface("k", fixtures.Interfaces)
|
||||
},
|
||||
"Interface(Object)": func(e *Event) *Event {
|
||||
return e.Interface("k", objects[0])
|
||||
return e.Interface("k", fixtures.Objects[0])
|
||||
},
|
||||
"Interface(Objects)": func(e *Event) *Event {
|
||||
return e.Interface("k", objects)
|
||||
return e.Interface("k", fixtures.Objects)
|
||||
},
|
||||
"Object": func(e *Event) *Event {
|
||||
return e.Object("k", objects[0])
|
||||
return e.Object("k", fixtures.Objects[0])
|
||||
},
|
||||
"Timestamp": func(e *Event) *Event {
|
||||
return e.Timestamp()
|
||||
},
|
||||
"IPAddr": func(e *Event) *Event {
|
||||
return e.IPAddr("k", ipAddrs[0])
|
||||
return e.IPAddr("k", fixtures.IPAddrs[0])
|
||||
},
|
||||
"IPAddrs": func(e *Event) *Event {
|
||||
return e.IPAddrs("k", ipAddrs)
|
||||
return e.IPAddrs("k", fixtures.IPAddrs)
|
||||
},
|
||||
"IPPrefix": func(e *Event) *Event {
|
||||
return e.IPPrefix("k", ipPfxs[0])
|
||||
return e.IPPrefix("k", fixtures.IPPfxs[0])
|
||||
},
|
||||
"IPPrefixes": func(e *Event) *Event {
|
||||
return e.IPPrefixes("k", ipPfxs)
|
||||
return e.IPPrefixes("k", fixtures.IPPfxs)
|
||||
},
|
||||
"MACAddr": func(e *Event) *Event {
|
||||
return e.MACAddr("k", macAddr)
|
||||
return e.MACAddr("k", fixtures.MACAddr)
|
||||
},
|
||||
"Type": func(e *Event) *Event {
|
||||
return e.Type("k", fixtures.Type)
|
||||
},
|
||||
}
|
||||
|
||||
logger := New(io.Discard)
|
||||
b.ResetTimer()
|
||||
for name := range types {
|
||||
@@ -266,145 +226,112 @@ func BenchmarkContextFieldType(b *testing.B) {
|
||||
TimeFieldFormat = TimeFormatUnix
|
||||
defer func() { TimeFieldFormat = oldFormat }()
|
||||
|
||||
bools := []bool{true, false, true, false, true, false, true, false, true, false}
|
||||
ints := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
|
||||
floats := []float64{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
|
||||
strings := []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"}
|
||||
stringer := net.IP{127, 0, 0, 1}
|
||||
durations := []time.Duration{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
|
||||
times := []time.Time{
|
||||
time.Unix(0, 0),
|
||||
time.Unix(1, 0),
|
||||
time.Unix(2, 0),
|
||||
time.Unix(3, 0),
|
||||
time.Unix(4, 0),
|
||||
time.Unix(5, 0),
|
||||
time.Unix(6, 0),
|
||||
time.Unix(7, 0),
|
||||
time.Unix(8, 0),
|
||||
time.Unix(9, 0),
|
||||
}
|
||||
interfaces := []struct {
|
||||
Pub string
|
||||
Tag string `json:"tag"`
|
||||
priv int
|
||||
}{
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
}
|
||||
objects := []obj{
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
}
|
||||
errs := []error{errors.New("a"), errors.New("b"), errors.New("c"), errors.New("d"), errors.New("e")}
|
||||
ipAddrV4 := net.IP{192, 168, 0, 1}
|
||||
ipAddrV6 := net.IP{0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x34}
|
||||
ipAddrs := []net.IP{ipAddrV4, ipAddrV6, ipAddrV4, ipAddrV6, ipAddrV4, ipAddrV6, ipAddrV4, ipAddrV6, ipAddrV4, ipAddrV6}
|
||||
ipPfxV4 := net.IPNet{IP: net.IP{192, 168, 0, 0}, Mask: net.CIDRMask(24, 32)}
|
||||
ipPfxV6 := net.IPNet{IP: net.IP{0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x00}, Mask: net.CIDRMask(64, 128)}
|
||||
ipPfxs := []net.IPNet{ipPfxV4, ipPfxV6, ipPfxV4, ipPfxV6, ipPfxV4, ipPfxV6, ipPfxV4, ipPfxV6, ipPfxV4, ipPfxV6}
|
||||
macAddr := net.HardwareAddr{0x00, 0x1A, 0x2B, 0x3C, 0x4D, 0x5E}
|
||||
|
||||
ctx := context.Background()
|
||||
fixtures := makeFieldFixtures()
|
||||
types := map[string]func(c Context) Context{
|
||||
"Any": func(c Context) Context {
|
||||
return c.Any("k", fixtures.Interfaces[0])
|
||||
},
|
||||
"Bool": func(c Context) Context {
|
||||
return c.Bool("k", bools[0])
|
||||
return c.Bool("k", fixtures.Bools[0])
|
||||
},
|
||||
"Bools": func(c Context) Context {
|
||||
return c.Bools("k", bools)
|
||||
return c.Bools("k", fixtures.Bools)
|
||||
},
|
||||
"Bytes": func(c Context) Context {
|
||||
return c.Bytes("k", fixtures.Bytes)
|
||||
},
|
||||
"Hex": func(c Context) Context {
|
||||
return c.Hex("k", fixtures.Bytes)
|
||||
},
|
||||
"Int": func(c Context) Context {
|
||||
return c.Int("k", ints[0])
|
||||
return c.Int("k", fixtures.Ints[0])
|
||||
},
|
||||
"Ints": func(c Context) Context {
|
||||
return c.Ints("k", ints)
|
||||
return c.Ints("k", fixtures.Ints)
|
||||
},
|
||||
"Float": func(c Context) Context {
|
||||
return c.Float64("k", floats[0])
|
||||
"Float32": func(c Context) Context {
|
||||
return c.Float32("k", fixtures.Floats32[0])
|
||||
},
|
||||
"Floats": func(c Context) Context {
|
||||
return c.Floats64("k", floats)
|
||||
"Floats32": func(c Context) Context {
|
||||
return c.Floats32("k", fixtures.Floats32)
|
||||
},
|
||||
"Float64": func(c Context) Context {
|
||||
return c.Float64("k", fixtures.Floats64[0])
|
||||
},
|
||||
"Floats64": func(c Context) Context {
|
||||
return c.Floats64("k", fixtures.Floats64)
|
||||
},
|
||||
"Str": func(c Context) Context {
|
||||
return c.Str("k", strings[0])
|
||||
return c.Str("k", fixtures.Strings[0])
|
||||
},
|
||||
"Strs": func(c Context) Context {
|
||||
return c.Strs("k", strings)
|
||||
return c.Strs("k", fixtures.Strings)
|
||||
},
|
||||
"Stringer": func(c Context) Context {
|
||||
return c.Stringer("k", stringer)
|
||||
return c.Stringer("k", fixtures.Stringers[0])
|
||||
},
|
||||
"Stringers": func(c Context) Context {
|
||||
return c.Stringers("k", fixtures.Stringers)
|
||||
},
|
||||
"Err": func(c Context) Context {
|
||||
return c.Err(errs[0])
|
||||
return c.Err(fixtures.Errs[0])
|
||||
},
|
||||
"Errs": func(c Context) Context {
|
||||
return c.Errs("k", errs)
|
||||
return c.Errs("k", fixtures.Errs)
|
||||
},
|
||||
"Ctx": func(c Context) Context {
|
||||
return c.Ctx(ctx)
|
||||
return c.Ctx(fixtures.Ctx)
|
||||
},
|
||||
"Time": func(c Context) Context {
|
||||
return c.Time("k", times[0])
|
||||
return c.Time("k", fixtures.Times[0])
|
||||
},
|
||||
"Times": func(c Context) Context {
|
||||
return c.Times("k", times)
|
||||
return c.Times("k", fixtures.Times)
|
||||
},
|
||||
"Dur": func(c Context) Context {
|
||||
return c.Dur("k", durations[0])
|
||||
return c.Dur("k", fixtures.Durations[0])
|
||||
},
|
||||
"Durs": func(c Context) Context {
|
||||
return c.Durs("k", durations)
|
||||
return c.Durs("k", fixtures.Durations)
|
||||
},
|
||||
"Interface": func(c Context) Context {
|
||||
return c.Interface("k", interfaces[0])
|
||||
return c.Interface("k", fixtures.Interfaces[0])
|
||||
},
|
||||
"Interfaces": func(c Context) Context {
|
||||
return c.Interface("k", interfaces)
|
||||
return c.Interface("k", fixtures.Interfaces)
|
||||
},
|
||||
"Interface(Object)": func(c Context) Context {
|
||||
return c.Interface("k", objects[0])
|
||||
return c.Interface("k", fixtures.Objects[0])
|
||||
},
|
||||
"Interface(Objects)": func(c Context) Context {
|
||||
return c.Interface("k", objects)
|
||||
return c.Interface("k", fixtures.Objects)
|
||||
},
|
||||
"Object": func(c Context) Context {
|
||||
return c.Object("k", objects[0])
|
||||
return c.Object("k", fixtures.Objects[0])
|
||||
},
|
||||
"Timestamp": func(c Context) Context {
|
||||
return c.Timestamp()
|
||||
},
|
||||
"IPAddr": func(c Context) Context {
|
||||
return c.IPAddr("k", ipAddrs[0])
|
||||
return c.IPAddr("k", fixtures.IPAddrs[0])
|
||||
},
|
||||
"IPAddrs": func(c Context) Context {
|
||||
return c.IPAddrs("k", ipAddrs)
|
||||
return c.IPAddrs("k", fixtures.IPAddrs)
|
||||
},
|
||||
"IPPrefix": func(c Context) Context {
|
||||
return c.IPPrefix("k", ipPfxs[0])
|
||||
return c.IPPrefix("k", fixtures.IPPfxs[0])
|
||||
},
|
||||
"IPPrefixes": func(c Context) Context {
|
||||
return c.IPPrefixes("k", ipPfxs)
|
||||
return c.IPPrefixes("k", fixtures.IPPfxs)
|
||||
},
|
||||
"MACAddr": func(c Context) Context {
|
||||
return c.MACAddr("k", macAddr)
|
||||
return c.MACAddr("k", fixtures.MACAddr)
|
||||
},
|
||||
"Type": func(c Context) Context {
|
||||
return c.Type("k", fixtures.Type)
|
||||
},
|
||||
}
|
||||
|
||||
logger := New(io.Discard)
|
||||
b.ResetTimer()
|
||||
for name := range types {
|
||||
|
||||
+12
@@ -96,6 +96,18 @@ func (c Context) Stringer(key string, val fmt.Stringer) Context {
|
||||
return c
|
||||
}
|
||||
|
||||
// Stringers adds the field key with vals as an array of strings by calling .String() on each entry
|
||||
// to the logger context.
|
||||
func (c Context) Stringers(key string, vals []fmt.Stringer) Context {
|
||||
if vals != nil {
|
||||
c.l.context = enc.AppendStringers(enc.AppendKey(c.l.context, key), vals)
|
||||
return c
|
||||
}
|
||||
|
||||
c.l.context = enc.AppendInterface(enc.AppendKey(c.l.context, key), nil)
|
||||
return c
|
||||
}
|
||||
|
||||
// Bytes adds the field key with val as a []byte to the logger context.
|
||||
func (c Context) Bytes(key string, val []byte) Context {
|
||||
c.l.context = enc.AppendBytes(enc.AppendKey(c.l.context, key), val)
|
||||
|
||||
+12
-3
@@ -5,6 +5,7 @@ import (
|
||||
"context"
|
||||
"io"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/rs/zerolog/internal/cbor"
|
||||
@@ -78,7 +79,15 @@ type logObjectMarshalerImpl struct {
|
||||
}
|
||||
|
||||
func (t logObjectMarshalerImpl) MarshalZerologObject(e *Event) {
|
||||
e.Str("name", "custom_value").Int("age", t.age)
|
||||
e.Str("name", strings.ToLower(t.name)).Int("age", -t.age)
|
||||
}
|
||||
|
||||
type errorObjectMarshalerImpl struct {
|
||||
error
|
||||
}
|
||||
|
||||
func (t errorObjectMarshalerImpl) MarshalZerologObject(e *Event) {
|
||||
e.Str("error", strings.ToUpper(t.error.Error()))
|
||||
}
|
||||
|
||||
func Test_InterfaceLogObjectMarshaler(t *testing.T) {
|
||||
@@ -89,13 +98,13 @@ func Test_InterfaceLogObjectMarshaler(t *testing.T) {
|
||||
log2 := Ctx(ctx)
|
||||
|
||||
withLog := log2.With().Interface("obj", &logObjectMarshalerImpl{
|
||||
name: "foo",
|
||||
name: "FOO",
|
||||
age: 29,
|
||||
}).Logger()
|
||||
|
||||
withLog.Info().Msg("test")
|
||||
|
||||
if got, want := cbor.DecodeIfBinaryToString(buf.Bytes()), `{"level":"info","obj":{"name":"custom_value","age":29},"message":"test"}`+"\n"; got != want {
|
||||
if got, want := cbor.DecodeIfBinaryToString(buf.Bytes()), `{"level":"info","obj":{"name":"foo","age":-29},"message":"test"}`+"\n"; got != want {
|
||||
t.Errorf("got %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
+238
@@ -38,6 +38,16 @@ func TestEvent_AnErr(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestEvent_writeWithNil(t *testing.T) {
|
||||
var e *Event = nil
|
||||
got := e.write()
|
||||
|
||||
var want *Event = nil
|
||||
if got != nil {
|
||||
t.Errorf("Event.write() = %v, want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEvent_ObjectWithNil(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
e := newEvent(LevelWriterAdapter{&buf}, DebugLevel)
|
||||
@@ -63,3 +73,231 @@ func TestEvent_EmbedObjectWithNil(t *testing.T) {
|
||||
t.Errorf("Event.EmbedObject() = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEvent_WithNilEvent(t *testing.T) {
|
||||
// coverage for nil Event receiver for all types
|
||||
var e *Event = nil
|
||||
|
||||
fixtures := makeFieldFixtures()
|
||||
types := map[string]func() *Event{
|
||||
"Array": func() *Event {
|
||||
arr := Arr()
|
||||
return e.Array("k", arr)
|
||||
},
|
||||
"Bool": func() *Event {
|
||||
return e.Bool("k", fixtures.Bools[0])
|
||||
},
|
||||
"Bools": func() *Event {
|
||||
return e.Bools("k", fixtures.Bools)
|
||||
},
|
||||
"Fields": func() *Event {
|
||||
return e.Fields(fixtures)
|
||||
},
|
||||
"Int": func() *Event {
|
||||
return e.Int("k", fixtures.Ints[0])
|
||||
},
|
||||
"Ints": func() *Event {
|
||||
return e.Ints("k", fixtures.Ints)
|
||||
},
|
||||
"Int8": func() *Event {
|
||||
return e.Int8("k", fixtures.Ints8[0])
|
||||
},
|
||||
"Ints8": func() *Event {
|
||||
return e.Ints8("k", fixtures.Ints8)
|
||||
},
|
||||
"Int16": func() *Event {
|
||||
return e.Int16("k", fixtures.Ints16[0])
|
||||
},
|
||||
"Ints16": func() *Event {
|
||||
return e.Ints16("k", fixtures.Ints16)
|
||||
},
|
||||
"Int32": func() *Event {
|
||||
return e.Int32("k", fixtures.Ints32[0])
|
||||
},
|
||||
"Ints32": func() *Event {
|
||||
return e.Ints32("k", fixtures.Ints32)
|
||||
},
|
||||
"Int64": func() *Event {
|
||||
return e.Int64("k", fixtures.Ints64[0])
|
||||
},
|
||||
"Ints64": func() *Event {
|
||||
return e.Ints64("k", fixtures.Ints64)
|
||||
},
|
||||
"Uint": func() *Event {
|
||||
return e.Uint("k", fixtures.Uints[0])
|
||||
},
|
||||
"Uints": func() *Event {
|
||||
return e.Uints("k", fixtures.Uints)
|
||||
},
|
||||
"Uint8": func() *Event {
|
||||
return e.Uint8("k", fixtures.Uints8[0])
|
||||
},
|
||||
"Uints8": func() *Event {
|
||||
return e.Uints8("k", fixtures.Uints8)
|
||||
},
|
||||
"Uint16": func() *Event {
|
||||
return e.Uint16("k", fixtures.Uints16[0])
|
||||
},
|
||||
"Uints16": func() *Event {
|
||||
return e.Uints16("k", fixtures.Uints16)
|
||||
},
|
||||
"Uint32": func() *Event {
|
||||
return e.Uint32("k", fixtures.Uints32[0])
|
||||
},
|
||||
"Uints32": func() *Event {
|
||||
return e.Uints32("k", fixtures.Uints32)
|
||||
},
|
||||
"Uint64": func() *Event {
|
||||
return e.Uint64("k", fixtures.Uints64[0])
|
||||
},
|
||||
"Uints64": func() *Event {
|
||||
return e.Uints64("k", fixtures.Uints64)
|
||||
},
|
||||
"Float64": func() *Event {
|
||||
return e.Float64("k", fixtures.Floats64[0])
|
||||
},
|
||||
"Floats64": func() *Event {
|
||||
return e.Floats64("k", fixtures.Floats64)
|
||||
},
|
||||
"Float32": func() *Event {
|
||||
return e.Float32("k", fixtures.Floats32[0])
|
||||
},
|
||||
"Floats32": func() *Event {
|
||||
return e.Floats32("k", fixtures.Floats32)
|
||||
},
|
||||
"RawCBOR": func() *Event {
|
||||
return e.RawCBOR("k", fixtures.RawCBOR)
|
||||
},
|
||||
"RawJSON": func() *Event {
|
||||
return e.RawJSON("k", fixtures.RawJSON)
|
||||
},
|
||||
"Str": func() *Event {
|
||||
return e.Str("k", fixtures.Strings[0])
|
||||
},
|
||||
"Strs": func() *Event {
|
||||
return e.Strs("k", fixtures.Strings)
|
||||
},
|
||||
"Stringers": func() *Event {
|
||||
return e.Stringers("k", fixtures.Stringers)
|
||||
},
|
||||
"Err": func() *Event {
|
||||
return e.Err(fixtures.Errs[0])
|
||||
},
|
||||
"Errs": func() *Event {
|
||||
return e.Errs("k", fixtures.Errs)
|
||||
},
|
||||
"Ctx": func() *Event {
|
||||
return e.Ctx(fixtures.Ctx)
|
||||
},
|
||||
"Time": func() *Event {
|
||||
return e.Time("k", fixtures.Times[0])
|
||||
},
|
||||
"Times": func() *Event {
|
||||
return e.Times("k", fixtures.Times)
|
||||
},
|
||||
"Dict": func() *Event {
|
||||
d := Dict()
|
||||
d.Str("greeting", "hello")
|
||||
return e.Dict("k", d)
|
||||
},
|
||||
"Dur": func() *Event {
|
||||
return e.Dur("k", fixtures.Durations[0])
|
||||
},
|
||||
"Durs": func() *Event {
|
||||
return e.Durs("k", fixtures.Durations)
|
||||
},
|
||||
"Interface": func() *Event {
|
||||
return e.Interface("k", fixtures.Interfaces[0])
|
||||
},
|
||||
"Interfaces": func() *Event {
|
||||
return e.Interface("k", fixtures.Interfaces)
|
||||
},
|
||||
"Interface(Object)": func() *Event {
|
||||
return e.Interface("k", fixtures.Objects[0])
|
||||
},
|
||||
"Interface(Objects)": func() *Event {
|
||||
return e.Interface("k", fixtures.Objects)
|
||||
},
|
||||
"Object": func() *Event {
|
||||
return e.Object("k", fixtures.Objects[0])
|
||||
},
|
||||
"EmbedObject": func() *Event {
|
||||
return e.EmbedObject(fixtures.Objects[0])
|
||||
},
|
||||
"Timestamp": func() *Event {
|
||||
return e.Timestamp()
|
||||
},
|
||||
"IPAddr": func() *Event {
|
||||
return e.IPAddr("k", fixtures.IPAddrs[0])
|
||||
},
|
||||
"IPAddrs": func() *Event {
|
||||
return e.IPAddrs("k", fixtures.IPAddrs)
|
||||
},
|
||||
"IPPrefix": func() *Event {
|
||||
return e.IPPrefix("k", fixtures.IPPfxs[0])
|
||||
},
|
||||
"IPPrefixes": func() *Event {
|
||||
return e.IPPrefixes("k", fixtures.IPPfxs)
|
||||
},
|
||||
"MACAddr": func() *Event {
|
||||
return e.MACAddr("k", fixtures.MACAddr)
|
||||
},
|
||||
"Type": func() *Event {
|
||||
return e.Type("k", fixtures.Type)
|
||||
},
|
||||
"Caller": func() *Event {
|
||||
return e.Caller(1)
|
||||
},
|
||||
"CallerSkip": func() *Event {
|
||||
return e.CallerSkipFrame(2)
|
||||
},
|
||||
"Stack": func() *Event {
|
||||
return e.Stack()
|
||||
},
|
||||
}
|
||||
|
||||
for name := range types {
|
||||
f := types[name]
|
||||
if got := f(); got != nil {
|
||||
t.Errorf("Event.Bool() = %v, want %v", got, nil)
|
||||
}
|
||||
}
|
||||
|
||||
e.Send()
|
||||
e.Msg("nothing")
|
||||
e.Msgf("what %s", "nothing")
|
||||
|
||||
got := e.write()
|
||||
if got != nil {
|
||||
t.Errorf("Event.write() = %v, want %v", got, e)
|
||||
}
|
||||
|
||||
called := false
|
||||
e.MsgFunc(func() string {
|
||||
called = true
|
||||
return "called"
|
||||
})
|
||||
if called {
|
||||
t.Errorf("Event.MsgFunc() should not be called on nil Event")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEvent_MsgFunc(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
e := newEvent(LevelWriterAdapter{&buf}, DebugLevel)
|
||||
|
||||
called := false
|
||||
e.MsgFunc(func() string {
|
||||
called = true
|
||||
return "called"
|
||||
})
|
||||
if !called {
|
||||
t.Errorf("Event.MsgFunc() was not called on non-nil Event")
|
||||
}
|
||||
|
||||
want := `{"message":"called"}`
|
||||
got := strings.TrimSpace(buf.String())
|
||||
if got != want {
|
||||
t.Errorf("Event.MsgFunc() = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,15 @@ func appendFields(dst []byte, fields interface{}, stack bool) []byte {
|
||||
return dst
|
||||
}
|
||||
|
||||
func appendObject(dst []byte, obj LogObjectMarshaler) []byte {
|
||||
e := newEvent(nil, 0)
|
||||
e.buf = e.buf[:0] // discard the beginning marker added by newEvent
|
||||
e.appendObject(obj)
|
||||
dst = append(dst, e.buf...)
|
||||
putEvent(e)
|
||||
return dst
|
||||
}
|
||||
|
||||
func appendFieldList(dst []byte, kvList []interface{}, stack bool) []byte {
|
||||
for i, n := 0, len(kvList); i < n; i += 2 {
|
||||
key, val := kvList[i], kvList[i+1]
|
||||
@@ -43,11 +52,7 @@ func appendFieldList(dst []byte, kvList []interface{}, stack bool) []byte {
|
||||
continue
|
||||
}
|
||||
if val, ok := val.(LogObjectMarshaler); ok {
|
||||
e := newEvent(nil, 0)
|
||||
e.buf = e.buf[:0]
|
||||
e.appendObject(val)
|
||||
dst = append(dst, e.buf...)
|
||||
putEvent(e)
|
||||
dst = appendObject(dst, val)
|
||||
continue
|
||||
}
|
||||
switch val := val.(type) {
|
||||
@@ -58,11 +63,7 @@ func appendFieldList(dst []byte, kvList []interface{}, stack bool) []byte {
|
||||
case error:
|
||||
switch m := ErrorMarshalFunc(val).(type) {
|
||||
case LogObjectMarshaler:
|
||||
e := newEvent(nil, 0)
|
||||
e.buf = e.buf[:0]
|
||||
e.appendObject(m)
|
||||
dst = append(dst, e.buf...)
|
||||
putEvent(e)
|
||||
dst = appendObject(dst, m)
|
||||
case error:
|
||||
if m == nil || isNilValue(m) {
|
||||
dst = enc.AppendNil(dst)
|
||||
@@ -94,11 +95,7 @@ func appendFieldList(dst []byte, kvList []interface{}, stack bool) []byte {
|
||||
for i, err := range val {
|
||||
switch m := ErrorMarshalFunc(err).(type) {
|
||||
case LogObjectMarshaler:
|
||||
e := newEvent(nil, 0)
|
||||
e.buf = e.buf[:0]
|
||||
e.appendObject(m)
|
||||
dst = append(dst, e.buf...)
|
||||
putEvent(e)
|
||||
dst = appendObject(dst, m)
|
||||
case error:
|
||||
if m == nil || isNilValue(m) {
|
||||
dst = enc.AppendNil(dst)
|
||||
@@ -112,7 +109,7 @@ func appendFieldList(dst []byte, kvList []interface{}, stack bool) []byte {
|
||||
}
|
||||
|
||||
if i < (len(val) - 1) {
|
||||
enc.AppendArrayDelim(dst)
|
||||
dst = enc.AppendArrayDelim(dst)
|
||||
}
|
||||
}
|
||||
dst = enc.AppendArrayEnd(dst)
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
package zerolog
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"reflect"
|
||||
"time"
|
||||
)
|
||||
|
||||
type fixtureObj struct {
|
||||
Pub string
|
||||
Tag string `json:"tag"`
|
||||
priv int
|
||||
}
|
||||
|
||||
func (o fixtureObj) MarshalZerologObject(e *Event) {
|
||||
e.Str("Pub", o.Pub).
|
||||
Str("Tag", o.Tag).
|
||||
Int("priv", o.priv)
|
||||
}
|
||||
|
||||
type fieldFixtures struct {
|
||||
Bools []bool
|
||||
Bytes []byte
|
||||
Ctx context.Context
|
||||
Durations []time.Duration
|
||||
Errs []error
|
||||
Floats32 []float32
|
||||
Floats64 []float64
|
||||
Interfaces []struct {
|
||||
Pub string
|
||||
Tag string `json:"tag"`
|
||||
priv int
|
||||
}
|
||||
Ints []int
|
||||
Ints8 []int8
|
||||
Ints16 []int16
|
||||
Ints32 []int32
|
||||
Ints64 []int64
|
||||
Uints []uint
|
||||
Uints8 []uint8
|
||||
Uints16 []uint16
|
||||
Uints32 []uint32
|
||||
Uints64 []uint64
|
||||
IPAddrs []net.IP
|
||||
IPPfxs []net.IPNet
|
||||
MACAddr net.HardwareAddr
|
||||
Objects []fixtureObj
|
||||
RawCBOR []byte
|
||||
RawJSON []byte
|
||||
Stringers []fmt.Stringer
|
||||
Strings []string
|
||||
Times []time.Time
|
||||
Type reflect.Type
|
||||
}
|
||||
|
||||
func makeFieldFixtures() *fieldFixtures {
|
||||
bools := []bool{true, false, true, false, true, false, true, false, true, false}
|
||||
bytes := []byte(`abcdef`)
|
||||
ints := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
|
||||
ints8 := []int8{-8, 8}
|
||||
ints16 := []int16{-16, 16}
|
||||
ints32 := []int32{-32, 32}
|
||||
ints64 := []int64{-64, 64}
|
||||
uints := []uint{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
|
||||
uints8 := []uint8{8, uint8(^uint8(0))}
|
||||
uints16 := []uint16{16, uint16(^uint16(0))}
|
||||
uints32 := []uint32{32, uint32(^uint32(0))}
|
||||
uints64 := []uint64{64, uint64(^uint64(0))}
|
||||
floats32 := []float32{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
|
||||
floats64 := []float64{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
|
||||
strings := []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"}
|
||||
durations := []time.Duration{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
|
||||
times := []time.Time{
|
||||
time.Unix(0, 0),
|
||||
time.Unix(1, 0),
|
||||
time.Unix(2, 0),
|
||||
time.Unix(3, 0),
|
||||
time.Unix(4, 0),
|
||||
time.Unix(5, 0),
|
||||
time.Unix(6, 0),
|
||||
time.Unix(7, 0),
|
||||
time.Unix(8, 0),
|
||||
time.Unix(9, 0),
|
||||
}
|
||||
interfaces := []struct {
|
||||
Pub string
|
||||
Tag string `json:"tag"`
|
||||
priv int
|
||||
}{
|
||||
{"A", "j", -5},
|
||||
{"B", "i", -4},
|
||||
{"C", "h", -3},
|
||||
{"D", "g", -2},
|
||||
{"E", "f", -1},
|
||||
{"F", "e", 0},
|
||||
{"G", "d", 1},
|
||||
{"H", "c", 2},
|
||||
{"I", "b", 3},
|
||||
{"J", "a", 4},
|
||||
}
|
||||
objects := []fixtureObj{
|
||||
{"a", "z", 1},
|
||||
{"b", "y", 2},
|
||||
{"c", "x", 3},
|
||||
{"d", "w", 4},
|
||||
{"e", "v", 5},
|
||||
{"f", "u", 6},
|
||||
{"g", "t", 7},
|
||||
{"h", "s", 8},
|
||||
{"i", "r", 9},
|
||||
{"j", "q", 10},
|
||||
}
|
||||
ipAddrV4 := net.IP{192, 168, 0, 1}
|
||||
ipAddrV6 := net.IP{0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x34}
|
||||
ipAddrs := []net.IP{ipAddrV4, ipAddrV6, ipAddrV4, ipAddrV6, ipAddrV4, ipAddrV6, ipAddrV4, ipAddrV6, ipAddrV4, ipAddrV6}
|
||||
ipPfxV4 := net.IPNet{IP: net.IP{192, 168, 0, 0}, Mask: net.CIDRMask(24, 32)}
|
||||
ipPfxV6 := net.IPNet{IP: net.IP{0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x00}, Mask: net.CIDRMask(64, 128)}
|
||||
ipPfxs := []net.IPNet{ipPfxV4, ipPfxV6, ipPfxV4, ipPfxV6, ipPfxV4, ipPfxV6, ipPfxV4, ipPfxV6, ipPfxV4, ipPfxV6}
|
||||
macAddr := net.HardwareAddr{0x00, 0x1A, 0x2B, 0x3C, 0x4D, 0x5E}
|
||||
errs := []error{errors.New("a"), errors.New("b"), errors.New("c"), errors.New("d"), errors.New("e"), nil, errorObjectMarshalerImpl{fmt.Errorf("oops")}}
|
||||
ctx := context.Background()
|
||||
stringers := []fmt.Stringer{ipAddrs[0], durations[0]}
|
||||
rawJSON := []byte(`{"some":"json"}`)
|
||||
rawCBOR := []byte{0xA1, 0x64, 0x73, 0x6F, 0x6D, 0x65, 0x64, 0x61, 0x74, 0x61} // {"some":"data"}
|
||||
|
||||
return &fieldFixtures{
|
||||
Bools: bools,
|
||||
Bytes: bytes,
|
||||
Ctx: ctx,
|
||||
Durations: durations,
|
||||
Errs: errs,
|
||||
Floats32: floats32,
|
||||
Floats64: floats64,
|
||||
Interfaces: interfaces,
|
||||
Ints: ints,
|
||||
Ints8: ints8,
|
||||
Ints16: ints16,
|
||||
Ints32: ints32,
|
||||
Ints64: ints64,
|
||||
Uints: uints,
|
||||
Uints8: uints8,
|
||||
Uints16: uints16,
|
||||
Uints32: uints32,
|
||||
Uints64: uints64,
|
||||
IPAddrs: ipAddrs,
|
||||
IPPfxs: ipPfxs,
|
||||
MACAddr: macAddr,
|
||||
Objects: objects,
|
||||
RawCBOR: rawCBOR,
|
||||
RawJSON: rawJSON,
|
||||
Stringers: stringers,
|
||||
Strings: strings,
|
||||
Times: times,
|
||||
Type: reflect.TypeOf(12345),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package cbor
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAppendKey(t *testing.T) {
|
||||
want := make([]byte, 0)
|
||||
want = append(want, 0xbf) // start string
|
||||
want = append(want, 0x63) // length 3
|
||||
want = append(want, []byte("key")...)
|
||||
|
||||
got := enc.AppendKey([]byte{}, "key")
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("AppendKey(%v)\ngot: 0x%s\nwant: 0x%s",
|
||||
"key",
|
||||
hex.EncodeToString(got),
|
||||
hex.EncodeToString(want))
|
||||
}
|
||||
}
|
||||
@@ -3,16 +3,19 @@ package cbor
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"math"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog/internal"
|
||||
)
|
||||
|
||||
func TestDecodeInteger(t *testing.T) {
|
||||
for _, tc := range integerTestCases {
|
||||
gotv := decodeInteger(getReader(tc.binary))
|
||||
if gotv != int64(tc.val) {
|
||||
for _, tc := range internal.IntegerTestCases {
|
||||
gotv := decodeInteger(getReader(tc.Binary))
|
||||
if gotv != int64(tc.Val) {
|
||||
t.Errorf("decodeInteger(0x%s)=0x%d, want: 0x%d",
|
||||
hex.EncodeToString([]byte(tc.binary)), gotv, tc.val)
|
||||
hex.EncodeToString([]byte(tc.Binary)), gotv, tc.Val)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,11 +31,11 @@ func TestDecodeString(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDecodeArray(t *testing.T) {
|
||||
for _, tc := range integerArrayTestCases {
|
||||
for _, tc := range internal.IntegerArrayTestCases {
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
array2Json(getReader(tc.binary), buf)
|
||||
if buf.String() != tc.json {
|
||||
t.Errorf("array2Json(0x%s)=%s, want: %s", hex.EncodeToString([]byte(tc.binary)), buf.String(), tc.json)
|
||||
array2Json(getReader(tc.Binary), buf)
|
||||
if buf.String() != tc.Json {
|
||||
t.Errorf("array2Json(0x%s)=%s, want: %s", hex.EncodeToString([]byte(tc.Binary)), buf.String(), tc.Json)
|
||||
}
|
||||
}
|
||||
//Unspecified Length Array
|
||||
@@ -53,27 +56,27 @@ func TestDecodeArray(t *testing.T) {
|
||||
t.Errorf("array2Json(0x%s)=%s, want: %s", hex.EncodeToString([]byte(tc.out)), buf.String(), tc.out)
|
||||
}
|
||||
}
|
||||
for _, tc := range booleanArrayTestCases {
|
||||
for _, tc := range internal.BooleanArrayTestCases {
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
array2Json(getReader(tc.binary), buf)
|
||||
if buf.String() != tc.json {
|
||||
t.Errorf("array2Json(0x%s)=%s, want: %s", hex.EncodeToString([]byte(tc.binary)), buf.String(), tc.json)
|
||||
array2Json(getReader(tc.Binary), buf)
|
||||
if buf.String() != tc.Json {
|
||||
t.Errorf("array2Json(0x%s)=%s, want: %s", hex.EncodeToString([]byte(tc.Binary)), buf.String(), tc.Json)
|
||||
}
|
||||
}
|
||||
//TODO add cases for arrays of other types
|
||||
}
|
||||
|
||||
var infiniteMapDecodeTestCases = []struct {
|
||||
bin []byte
|
||||
json string
|
||||
Bin []byte
|
||||
Json string
|
||||
}{
|
||||
{[]byte("\xbf\x64IETF\x20\xff"), "{\"IETF\":-1}"},
|
||||
{[]byte("\xbf\x65Array\x84\x20\x00\x18\xc8\x14\xff"), "{\"Array\":[-1,0,200,20]}"},
|
||||
}
|
||||
|
||||
var mapDecodeTestCases = []struct {
|
||||
bin []byte
|
||||
json string
|
||||
Bin []byte
|
||||
Json string
|
||||
}{
|
||||
{[]byte("\xa2\x64IETF\x20"), "{\"IETF\":-1}"},
|
||||
{[]byte("\xa2\x65Array\x84\x20\x00\x18\xc8\x14"), "{\"Array\":[-1,0,200,20]}"},
|
||||
@@ -82,90 +85,90 @@ var mapDecodeTestCases = []struct {
|
||||
func TestDecodeMap(t *testing.T) {
|
||||
for _, tc := range mapDecodeTestCases {
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
map2Json(getReader(string(tc.bin)), buf)
|
||||
if buf.String() != tc.json {
|
||||
t.Errorf("map2Json(0x%s)=%s, want: %s", hex.EncodeToString(tc.bin), buf.String(), tc.json)
|
||||
map2Json(getReader(string(tc.Bin)), buf)
|
||||
if buf.String() != tc.Json {
|
||||
t.Errorf("map2Json(0x%s)=%s, want: %s", hex.EncodeToString(tc.Bin), buf.String(), tc.Json)
|
||||
}
|
||||
}
|
||||
for _, tc := range infiniteMapDecodeTestCases {
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
map2Json(getReader(string(tc.bin)), buf)
|
||||
if buf.String() != tc.json {
|
||||
t.Errorf("map2Json(0x%s)=%s, want: %s", hex.EncodeToString(tc.bin), buf.String(), tc.json)
|
||||
map2Json(getReader(string(tc.Bin)), buf)
|
||||
if buf.String() != tc.Json {
|
||||
t.Errorf("map2Json(0x%s)=%s, want: %s", hex.EncodeToString(tc.Bin), buf.String(), tc.Json)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeBool(t *testing.T) {
|
||||
for _, tc := range booleanTestCases {
|
||||
got := decodeSimpleFloat(getReader(tc.binary))
|
||||
if string(got) != tc.json {
|
||||
t.Errorf("decodeSimpleFloat(0x%s)=%s, want:%s", hex.EncodeToString([]byte(tc.binary)), string(got), tc.json)
|
||||
for _, tc := range internal.BooleanTestCases {
|
||||
got := decodeSimpleFloat(getReader(tc.Binary))
|
||||
if string(got) != tc.Json {
|
||||
t.Errorf("decodeSimpleFloat(0x%s)=%s, want:%s", hex.EncodeToString([]byte(tc.Binary)), string(got), tc.Json)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeFloat(t *testing.T) {
|
||||
for _, tc := range float32TestCases {
|
||||
got, _ := decodeFloat(getReader(tc.binary))
|
||||
if got != float64(tc.val) {
|
||||
t.Errorf("decodeFloat(0x%s)=%f, want:%f", hex.EncodeToString([]byte(tc.binary)), got, tc.val)
|
||||
for _, tc := range internal.Float32TestCases {
|
||||
got, _ := decodeFloat(getReader(tc.Binary))
|
||||
if got != float64(tc.Val) && math.IsNaN(got) != math.IsNaN(float64(tc.Val)) {
|
||||
t.Errorf("decodeFloat(0x%s)=%f, want:%f", hex.EncodeToString([]byte(tc.Binary)), got, tc.Val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeTimestamp(t *testing.T) {
|
||||
decodeTimeZone, _ = time.LoadLocation("UTC")
|
||||
for _, tc := range timeIntegerTestcases {
|
||||
tm := decodeTagData(getReader(tc.binary))
|
||||
if string(tm) != "\""+tc.rfcStr+"\"" {
|
||||
t.Errorf("decodeFloat(0x%s)=%s, want:%s", hex.EncodeToString([]byte(tc.binary)), tm, tc.rfcStr)
|
||||
for _, tc := range internal.TimeIntegerTestcases {
|
||||
tm := decodeTagData(getReader(tc.Binary))
|
||||
if string(tm) != "\""+tc.RfcStr+"\"" {
|
||||
t.Errorf("decodeFloat(0x%s)=%s, want:%s", hex.EncodeToString([]byte(tc.Binary)), tm, tc.RfcStr)
|
||||
}
|
||||
}
|
||||
for _, tc := range timeFloatTestcases {
|
||||
tm := decodeTagData(getReader(tc.out))
|
||||
for _, tc := range internal.TimeFloatTestcases {
|
||||
tm := decodeTagData(getReader(tc.Out))
|
||||
//Since we convert to float and back - it may be slightly off - so
|
||||
//we cannot check for exact equality instead, we'll check it is
|
||||
//very close to each other Less than a Microsecond (lets not yet do nanosec)
|
||||
|
||||
got, _ := time.Parse(string(tm), string(tm))
|
||||
want, _ := time.Parse(tc.rfcStr, tc.rfcStr)
|
||||
want, _ := time.Parse(tc.RfcStr, tc.RfcStr)
|
||||
if got.Sub(want) > time.Microsecond {
|
||||
t.Errorf("decodeFloat(0x%s)=%s, want:%s", hex.EncodeToString([]byte(tc.out)), tm, tc.rfcStr)
|
||||
t.Errorf("decodeFloat(0x%s)=%s, want:%s", hex.EncodeToString([]byte(tc.Out)), tm, tc.RfcStr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeNetworkAddr(t *testing.T) {
|
||||
for _, tc := range ipAddrTestCases {
|
||||
d1 := decodeTagData(getReader(tc.binary))
|
||||
if string(d1) != tc.text {
|
||||
t.Errorf("decodeNetworkAddr(0x%s)=%s, want:%s", hex.EncodeToString([]byte(tc.binary)), d1, tc.text)
|
||||
for _, tc := range internal.IpAddrTestCases {
|
||||
d1 := decodeTagData(getReader(tc.Binary))
|
||||
if string(d1) != tc.Text {
|
||||
t.Errorf("decodeNetworkAddr(0x%s)=%s, want:%s", hex.EncodeToString([]byte(tc.Binary)), d1, tc.Text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeMACAddr(t *testing.T) {
|
||||
for _, tc := range macAddrTestCases {
|
||||
d1 := decodeTagData(getReader(tc.binary))
|
||||
if string(d1) != tc.text {
|
||||
t.Errorf("decodeNetworkAddr(0x%s)=%s, want:%s", hex.EncodeToString([]byte(tc.binary)), d1, tc.text)
|
||||
for _, tc := range internal.MacAddrTestCases {
|
||||
d1 := decodeTagData(getReader(tc.Binary))
|
||||
if string(d1) != tc.Text {
|
||||
t.Errorf("decodeNetworkAddr(0x%s)=%s, want:%s", hex.EncodeToString([]byte(tc.Binary)), d1, tc.Text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeIPPrefix(t *testing.T) {
|
||||
for _, tc := range IPPrefixTestCases {
|
||||
d1 := decodeTagData(getReader(tc.binary))
|
||||
if string(d1) != tc.text {
|
||||
t.Errorf("decodeIPPrefix(0x%s)=%s, want:%s", hex.EncodeToString([]byte(tc.binary)), d1, tc.text)
|
||||
for _, tc := range internal.IPPrefixTestCases {
|
||||
d1 := decodeTagData(getReader(tc.Binary))
|
||||
if string(d1) != tc.Text {
|
||||
t.Errorf("decodeIPPrefix(0x%s)=%s, want:%s", hex.EncodeToString([]byte(tc.Binary)), d1, tc.Text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var compositeCborTestCases = []struct {
|
||||
binary []byte
|
||||
json string
|
||||
Binary []byte
|
||||
Json string
|
||||
}{
|
||||
{[]byte("\xbf\x64IETF\x20\x65Array\x9f\x20\x00\x18\xc8\x14\xff\xff"), "{\"IETF\":-1,\"Array\":[-1,0,200,20]}\n"},
|
||||
{[]byte("\xbf\x64IETF\x64YES!\x65Array\x9f\x20\x00\x18\xc8\x14\xff\xff"), "{\"IETF\":\"YES!\",\"Array\":[-1,0,200,20]}\n"},
|
||||
@@ -174,15 +177,15 @@ var compositeCborTestCases = []struct {
|
||||
func TestDecodeCbor2Json(t *testing.T) {
|
||||
for _, tc := range compositeCborTestCases {
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
err := Cbor2JsonManyObjects(getReader(string(tc.binary)), buf)
|
||||
if buf.String() != tc.json || err != nil {
|
||||
t.Errorf("cbor2JsonManyObjects(0x%s)=%s, want: %s, err:%s", hex.EncodeToString(tc.binary), buf.String(), tc.json, err.Error())
|
||||
err := Cbor2JsonManyObjects(getReader(string(tc.Binary)), buf)
|
||||
if buf.String() != tc.Json || err != nil {
|
||||
t.Errorf("cbor2JsonManyObjects(0x%s)=%s, want: %s, err:%s", hex.EncodeToString(tc.Binary), buf.String(), tc.Json, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var negativeCborTestCases = []struct {
|
||||
binary []byte
|
||||
Binary []byte
|
||||
errStr string
|
||||
}{
|
||||
{[]byte("\xb9\x64IETF\x20\x65Array\x9f\x20\x00\x18\xc8\x14"), "Tried to Read 18 Bytes.. But hit end of file"},
|
||||
@@ -197,7 +200,7 @@ var negativeCborTestCases = []struct {
|
||||
func TestDecodeNegativeCbor2Json(t *testing.T) {
|
||||
for _, tc := range negativeCborTestCases {
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
err := Cbor2JsonManyObjects(getReader(string(tc.binary)), buf)
|
||||
err := Cbor2JsonManyObjects(getReader(string(tc.Binary)), buf)
|
||||
if err == nil || err.Error() != tc.errStr {
|
||||
t.Errorf("Expected error got:%s, want:%s", err, tc.errStr)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package cbor
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -12,6 +13,13 @@ var encodeStringTests = []struct {
|
||||
}{
|
||||
{"", "\x60", ""},
|
||||
{"\\", "\x61\x5c", "\\\\"},
|
||||
{"\"", "\x61\x22", "\\\""},
|
||||
{"\b", "\x61\x08", "\\b"},
|
||||
{"\f", "\x61\x0c", "\\f"},
|
||||
{"\n", "\x61\x0a", "\\n"},
|
||||
{"\r", "\x61\x0d", "\\r"},
|
||||
{"\t", "\x61\x09", "\\t"},
|
||||
{"Hi\t", "\x63Hi\x09", "Hi\\t"},
|
||||
{"\x00", "\x61\x00", "\\u0000"},
|
||||
{"\x01", "\x61\x01", "\\u0001"},
|
||||
{"\x02", "\x61\x02", "\\u0002"},
|
||||
@@ -44,6 +52,9 @@ var encodeByteTests = []struct {
|
||||
{[]byte("\x02"), "\x41\x02"},
|
||||
{[]byte("\x03"), "\x41\x03"},
|
||||
{[]byte("\x04"), "\x41\x04"},
|
||||
{[]byte("\f"), "\x41\x0C"},
|
||||
{[]byte("\n"), "\x41\x0A"},
|
||||
{[]byte("\r"), "\x41\x0D"},
|
||||
{[]byte("*"), "\x41*"},
|
||||
{[]byte("a"), "\x41a"},
|
||||
{[]byte("IETF"), "\x44IETF"},
|
||||
@@ -77,6 +88,53 @@ func TestAppendString(t *testing.T) {
|
||||
t.Errorf("appendString(%q) = %#q, want %#q", inp, got, want)
|
||||
}
|
||||
}
|
||||
func TestAppendStrings(t *testing.T) {
|
||||
array := []string{}
|
||||
for _, tt := range encodeStringTests {
|
||||
array = append(array, tt.plain)
|
||||
}
|
||||
want := make([]byte, 0)
|
||||
want = append(want, 0x94) // start array length 24
|
||||
for _, tt := range encodeStringTests {
|
||||
want = append(want, []byte(tt.binary)...)
|
||||
}
|
||||
|
||||
got := enc.AppendStrings([]byte{}, array)
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("AppendStrings(%v)\ngot: 0x%s\nwant: 0x%s",
|
||||
array,
|
||||
hex.EncodeToString(got),
|
||||
hex.EncodeToString(want))
|
||||
}
|
||||
|
||||
// now empty array case
|
||||
array = make([]string, 0)
|
||||
want = make([]byte, 0)
|
||||
want = append(want, 0x80) // start an empty string array
|
||||
got = enc.AppendStrings([]byte{}, array)
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("AppendStrings(%v)\ngot: 0x%s\nwant: 0x%s",
|
||||
array, hex.EncodeToString(got),
|
||||
hex.EncodeToString(want))
|
||||
}
|
||||
|
||||
// now large array case
|
||||
array = make([]string, 24)
|
||||
want = make([]byte, 0)
|
||||
want = append(want, 0x98) // start a large array
|
||||
want = append(want, 0x18) // of length 24
|
||||
for i := 0; i < len(array); i++ {
|
||||
array[i] = "test"
|
||||
want = append(want, []byte("\x64test")...)
|
||||
}
|
||||
got = enc.AppendStrings([]byte{}, array)
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("AppendStrings(%v)\ngot: 0x%s\nwant: 0x%s",
|
||||
array,
|
||||
hex.EncodeToString(got),
|
||||
hex.EncodeToString(want))
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppendBytes(t *testing.T) {
|
||||
for _, tt := range encodeByteTests {
|
||||
|
||||
+138
-28
@@ -1,11 +1,14 @@
|
||||
package cbor
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"math"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog/internal"
|
||||
)
|
||||
|
||||
func TestAppendTimeNow(t *testing.T) {
|
||||
@@ -13,7 +16,7 @@ func TestAppendTimeNow(t *testing.T) {
|
||||
s := enc.AppendTime([]byte{}, tm, "unused")
|
||||
got := string(s)
|
||||
|
||||
tm1 := float64(tm.Unix()) + float64(tm.Nanosecond())*1E-9
|
||||
tm1 := float64(tm.Unix()) + float64(tm.Nanosecond())*1e-9
|
||||
tm2 := math.Float64bits(tm1)
|
||||
var tm3 [8]byte
|
||||
for i := uint(0); i < 8; i++ {
|
||||
@@ -27,55 +30,162 @@ func TestAppendTimeNow(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
var timeIntegerTestcases = []struct {
|
||||
txt string
|
||||
binary string
|
||||
rfcStr string
|
||||
}{
|
||||
{"2013-02-03T19:54:00-08:00", "\xc1\x1a\x51\x0f\x30\xd8", "2013-02-04T03:54:00Z"},
|
||||
{"1950-02-03T19:54:00-08:00", "\xc1\x3a\x25\x71\x93\xa7", "1950-02-04T03:54:00Z"},
|
||||
}
|
||||
|
||||
func TestAppendTimePastPresentInteger(t *testing.T) {
|
||||
for _, tt := range timeIntegerTestcases {
|
||||
tin, err := time.Parse(time.RFC3339, tt.txt)
|
||||
for _, tt := range internal.TimeIntegerTestcases {
|
||||
tin, err := time.Parse(time.RFC3339, tt.Txt)
|
||||
if err != nil {
|
||||
fmt.Println("Cannot parse input", tt.txt, ".. Skipping!", err)
|
||||
fmt.Println("Cannot parse input", tt.Txt, ".. Skipping!", err)
|
||||
continue
|
||||
}
|
||||
b := enc.AppendTime([]byte{}, tin, "unused")
|
||||
if got, want := string(b), tt.binary; got != want {
|
||||
t.Errorf("appendString(%s) = 0x%s, want 0x%s", tt.txt,
|
||||
if got, want := string(b), tt.Binary; got != want {
|
||||
t.Errorf("appendString(%s) = 0x%s, want 0x%s", tt.Txt,
|
||||
hex.EncodeToString(b),
|
||||
hex.EncodeToString([]byte(want)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var timeFloatTestcases = []struct {
|
||||
rfcStr string
|
||||
out string
|
||||
}{
|
||||
{"2006-01-02T15:04:05.999999-08:00", "\xc1\xfb\x41\xd0\xee\x6c\x59\x7f\xff\xfc"},
|
||||
{"1956-01-02T15:04:05.999999-08:00", "\xc1\xfb\xc1\xba\x53\x81\x1a\x00\x00\x11"},
|
||||
}
|
||||
|
||||
func TestAppendTimePastPresentFloat(t *testing.T) {
|
||||
const timeFloatFmt = "2006-01-02T15:04:05.999999-07:00"
|
||||
for _, tt := range timeFloatTestcases {
|
||||
tin, err := time.Parse(timeFloatFmt, tt.rfcStr)
|
||||
for _, tt := range internal.TimeFloatTestcases {
|
||||
tin, err := time.Parse(timeFloatFmt, tt.RfcStr)
|
||||
if err != nil {
|
||||
fmt.Println("Cannot parse input", tt.rfcStr, ".. Skipping!")
|
||||
fmt.Println("Cannot parse input", tt.RfcStr, ".. Skipping!")
|
||||
continue
|
||||
}
|
||||
b := enc.AppendTime([]byte{}, tin, "unused")
|
||||
if got, want := string(b), tt.out; got != want {
|
||||
t.Errorf("appendString(%s) = 0x%s, want 0x%s", tt.rfcStr,
|
||||
if got, want := string(b), tt.Out; got != want {
|
||||
t.Errorf("appendString(%s) = 0x%s, want 0x%s", tt.RfcStr,
|
||||
hex.EncodeToString(b),
|
||||
hex.EncodeToString([]byte(want)))
|
||||
}
|
||||
}
|
||||
}
|
||||
func TestAppendTimes(t *testing.T) {
|
||||
const timeFloatFmt = "2006-01-02T15:04:05.999999-07:00"
|
||||
array := make([]time.Time, len(internal.TimeFloatTestcases))
|
||||
want := make([]byte, 0)
|
||||
want = append(want, 0x82) // start small array
|
||||
for i, tt := range internal.TimeFloatTestcases {
|
||||
array[i], _ = time.Parse(timeFloatFmt, tt.RfcStr)
|
||||
want = append(want, []byte(tt.Out)...)
|
||||
}
|
||||
|
||||
got := enc.AppendTimes([]byte{}, array, "unused")
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("AppendTimes(%v)\ngot: 0x%s\nwant: 0x%s",
|
||||
array, hex.EncodeToString(got),
|
||||
hex.EncodeToString(want))
|
||||
}
|
||||
|
||||
// now empty array case
|
||||
array = make([]time.Time, 0)
|
||||
want = make([]byte, 0)
|
||||
want = append(want, 0x9f) // start and end array
|
||||
want = append(want, 0xff) // for empty array
|
||||
got = enc.AppendTimes([]byte{}, array, "unused")
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("AppendTimes(%v)\ngot: 0x%s\nwant: 0x%s",
|
||||
array, hex.EncodeToString(got),
|
||||
hex.EncodeToString(want))
|
||||
}
|
||||
|
||||
// now large array case
|
||||
testtime, _ := time.Parse(timeFloatFmt, internal.TimeFloatTestcases[0].RfcStr)
|
||||
outbytes := internal.TimeFloatTestcases[0].Out
|
||||
array = make([]time.Time, 24)
|
||||
want = make([]byte, 0)
|
||||
want = append(want, 0x98) // start a large array
|
||||
want = append(want, 0x18) // of length 24
|
||||
for i := 0; i < len(array); i++ {
|
||||
array[i] = testtime
|
||||
want = append(want, []byte(outbytes)...)
|
||||
}
|
||||
got = enc.AppendTimes([]byte{}, array, "unused")
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("AppendTimes(%v)\ngot: 0x%s\nwant: 0x%s",
|
||||
array,
|
||||
hex.EncodeToString(got),
|
||||
hex.EncodeToString(want))
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppendDurationFloat(t *testing.T) {
|
||||
for _, tt := range internal.DurTestcases {
|
||||
dur := tt.Duration
|
||||
want := []byte{}
|
||||
want = append(want, []byte(tt.FloatOut)...)
|
||||
got := enc.AppendDuration([]byte{}, dur, time.Microsecond, false, -1)
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("AppendDuration(%v)=\ngot: 0x%s\nwant: 0x%s",
|
||||
dur,
|
||||
hex.EncodeToString(got),
|
||||
hex.EncodeToString(want))
|
||||
}
|
||||
}
|
||||
}
|
||||
func TestAppendDurationInteger(t *testing.T) {
|
||||
for _, tt := range internal.DurTestcases {
|
||||
dur := tt.Duration
|
||||
want := []byte{}
|
||||
want = append(want, []byte(tt.IntegerOut)...)
|
||||
got := enc.AppendDuration([]byte{}, dur, time.Microsecond, true, -1)
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("AppendDuration(%v)=\ngot: 0x%s\nwant: 0x%s",
|
||||
dur,
|
||||
hex.EncodeToString(got),
|
||||
hex.EncodeToString(want))
|
||||
}
|
||||
}
|
||||
}
|
||||
func TestAppendDurations(t *testing.T) {
|
||||
array := make([]time.Duration, len(internal.DurTestcases))
|
||||
want := make([]byte, 0)
|
||||
want = append(want, 0x83) // start 3 element array
|
||||
for i, tt := range internal.DurTestcases {
|
||||
array[i] = tt.Duration
|
||||
want = append(want, []byte(tt.FloatOut)...)
|
||||
}
|
||||
|
||||
got := enc.AppendDurations([]byte{}, array, time.Microsecond, false, -1)
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("AppendDurations(%v)\ngot: 0x%s\nwant: 0x%s",
|
||||
array, hex.EncodeToString(got),
|
||||
hex.EncodeToString(want))
|
||||
}
|
||||
|
||||
// now empty array case
|
||||
array = make([]time.Duration, 0)
|
||||
want = make([]byte, 0)
|
||||
want = append(want, 0x9f) // start and end array
|
||||
want = append(want, 0xff) // for empty array
|
||||
got = enc.AppendDurations([]byte{}, array, time.Microsecond, false, -1)
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("AppendDurations(%v)\ngot: 0x%s\nwant: 0x%s",
|
||||
array, hex.EncodeToString(got),
|
||||
hex.EncodeToString(want))
|
||||
}
|
||||
|
||||
// now large array case
|
||||
testtime := internal.DurTestcases[0].Duration
|
||||
outbytes := internal.DurTestcases[0].FloatOut
|
||||
array = make([]time.Duration, 24)
|
||||
want = make([]byte, 0)
|
||||
want = append(want, 0x98) // start a large array
|
||||
want = append(want, 0x18) // of length 24
|
||||
for i := 0; i < len(array); i++ {
|
||||
array[i] = testtime
|
||||
want = append(want, []byte(outbytes)...)
|
||||
}
|
||||
got = enc.AppendDurations([]byte{}, array, time.Microsecond, false, -1)
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("AppendDurations(%v)\ngot: 0x%s\nwant: 0x%s",
|
||||
array,
|
||||
hex.EncodeToString(got),
|
||||
hex.EncodeToString(want))
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAppendTime(b *testing.B) {
|
||||
tests := map[string]string{
|
||||
|
||||
+946
-200
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,30 @@
|
||||
package json
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAppendKey(t *testing.T) {
|
||||
want := make([]byte, 0)
|
||||
want = append(want, []byte("{\"key\":")...)
|
||||
|
||||
got := enc.AppendKey([]byte("{"), "key") // test with empty object
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("AppendKey(%v)\ngot: %s\nwant: %s",
|
||||
"key",
|
||||
string(got),
|
||||
string(want))
|
||||
}
|
||||
|
||||
want = make([]byte, 0)
|
||||
want = append(want, []byte("},\"key\":")...) // test with non-empty object
|
||||
|
||||
got = enc.AppendKey([]byte("}"), "key")
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("AppendKey(%v)\ngot: %s\nwant: %s",
|
||||
"key",
|
||||
string(got),
|
||||
string(want))
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ func (Encoder) AppendBytes(dst, s []byte) []byte {
|
||||
func (Encoder) AppendHex(dst, s []byte) []byte {
|
||||
dst = append(dst, '"')
|
||||
for _, v := range s {
|
||||
dst = append(dst, hex[v>>4], hex[v&0x0f])
|
||||
dst = append(dst, hexCharacters[v>>4], hexCharacters[v&0x0f])
|
||||
}
|
||||
return append(dst, '"')
|
||||
}
|
||||
@@ -73,7 +73,7 @@ func appendBytesComplex(dst, s []byte, i int) []byte {
|
||||
case '\t':
|
||||
dst = append(dst, '\\', 't')
|
||||
default:
|
||||
dst = append(dst, '\\', 'u', '0', '0', hex[b>>4], hex[b&0xF])
|
||||
dst = append(dst, '\\', 'u', '0', '0', hexCharacters[b>>4], hexCharacters[b&0xF])
|
||||
}
|
||||
i++
|
||||
start = i
|
||||
|
||||
@@ -3,24 +3,26 @@ package json
|
||||
import (
|
||||
"testing"
|
||||
"unicode"
|
||||
|
||||
"github.com/rs/zerolog/internal"
|
||||
)
|
||||
|
||||
var enc = Encoder{}
|
||||
|
||||
func TestAppendBytes(t *testing.T) {
|
||||
for _, tt := range encodeStringTests {
|
||||
b := enc.AppendBytes([]byte{}, []byte(tt.in))
|
||||
if got, want := string(b), tt.out; got != want {
|
||||
t.Errorf("appendBytes(%q) = %#q, want %#q", tt.in, got, want)
|
||||
for _, tt := range internal.EncodeStringTests {
|
||||
b := enc.AppendBytes([]byte{}, []byte(tt.In))
|
||||
if got, want := string(b), tt.Out; got != want {
|
||||
t.Errorf("appendBytes(%q) = %#q, want %#q", tt.In, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppendHex(t *testing.T) {
|
||||
for _, tt := range encodeHexTests {
|
||||
b := enc.AppendHex([]byte{}, []byte{tt.in})
|
||||
if got, want := string(b), tt.out; got != want {
|
||||
t.Errorf("appendHex(%x) = %s, want %s", tt.in, got, want)
|
||||
for _, tt := range internal.EncodeHexTests {
|
||||
b := enc.AppendHex([]byte{}, []byte{tt.In})
|
||||
if got, want := string(b), tt.Out; got != want {
|
||||
t.Errorf("appendHex(%x) = %s, want %s", tt.In, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,433 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,356 @@
|
||||
package json
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
"github.com/rs/zerolog/internal"
|
||||
)
|
||||
|
||||
func TestAppendInts8(t *testing.T) {
|
||||
doOne := func(vals []int8) {
|
||||
want := make([]byte, 0)
|
||||
want = append(want, '[')
|
||||
for i, val := range vals {
|
||||
want = append(want, []byte(fmt.Sprintf("%d", int8(val)))...)
|
||||
if i < len(vals)-1 {
|
||||
want = append(want, ',')
|
||||
}
|
||||
}
|
||||
want = append(want, ']')
|
||||
|
||||
got := enc.AppendInts8([]byte{}, vals)
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("AppendInts8(%v)\ngot: %s\nwant: %s",
|
||||
vals,
|
||||
string(got),
|
||||
string(want))
|
||||
}
|
||||
}
|
||||
|
||||
array := make([]int8, 0)
|
||||
for _, tc := range internal.IntegerTestCases {
|
||||
if (tc.Val < math.MinInt8) || (tc.Val > math.MaxInt8) {
|
||||
continue
|
||||
}
|
||||
array = append(array, int8(tc.Val))
|
||||
}
|
||||
|
||||
doOne(array)
|
||||
doOne(array[:1]) // single element
|
||||
doOne(array[:0]) // edge case of zero length
|
||||
}
|
||||
func TestAppendUints8(t *testing.T) {
|
||||
doOne := func(vals []uint8) {
|
||||
want := make([]byte, 0)
|
||||
want = append(want, '[')
|
||||
for i, val := range vals {
|
||||
want = append(want, []byte(fmt.Sprintf("%v", uint8(val)))...)
|
||||
if i < len(vals)-1 {
|
||||
want = append(want, ',')
|
||||
}
|
||||
}
|
||||
want = append(want, ']')
|
||||
|
||||
got := enc.AppendUints8([]byte{}, vals)
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("AppendUints8(%v)\ngot: %s\nwant: %s",
|
||||
vals,
|
||||
string(got),
|
||||
string(want))
|
||||
}
|
||||
}
|
||||
|
||||
array := make([]uint8, 0)
|
||||
for _, tc := range internal.UnsignedIntegerTestCases {
|
||||
if tc.Val > math.MaxUint8 {
|
||||
continue
|
||||
}
|
||||
array = append(array, uint8(tc.Val))
|
||||
}
|
||||
|
||||
doOne(array)
|
||||
doOne(array[:1]) // single element
|
||||
doOne(array[:0]) // edge case of zero length
|
||||
}
|
||||
func TestAppendInts16(t *testing.T) {
|
||||
doOne := func(vals []int16) {
|
||||
want := make([]byte, 0)
|
||||
want = append(want, '[')
|
||||
for i, val := range vals {
|
||||
want = append(want, []byte(fmt.Sprintf("%d", int16(val)))...)
|
||||
if i < len(vals)-1 {
|
||||
want = append(want, ',')
|
||||
}
|
||||
}
|
||||
want = append(want, ']')
|
||||
|
||||
got := enc.AppendInts16([]byte{}, vals)
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("AppendInts16(%v)\ngot: %s\nwant: %s",
|
||||
vals,
|
||||
string(got),
|
||||
string(want))
|
||||
}
|
||||
}
|
||||
|
||||
array := make([]int16, 0)
|
||||
for _, tc := range internal.IntegerTestCases {
|
||||
if (tc.Val < math.MinInt16) || (tc.Val > math.MaxInt16) {
|
||||
continue
|
||||
}
|
||||
array = append(array, int16(tc.Val))
|
||||
}
|
||||
|
||||
doOne(array)
|
||||
doOne(array[:1]) // single element
|
||||
doOne(array[:0]) // edge case of zero length
|
||||
}
|
||||
func TestAppendUints16(t *testing.T) {
|
||||
doOne := func(vals []uint16) {
|
||||
want := make([]byte, 0)
|
||||
want = append(want, '[')
|
||||
for i, val := range vals {
|
||||
want = append(want, []byte(fmt.Sprintf("%d", uint16(val)))...)
|
||||
if i < len(vals)-1 {
|
||||
want = append(want, ',')
|
||||
}
|
||||
}
|
||||
want = append(want, ']')
|
||||
|
||||
got := enc.AppendUints16([]byte{}, vals)
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("AppendUints16(%v)\ngot: %s\nwant: %s",
|
||||
vals,
|
||||
string(got),
|
||||
string(want))
|
||||
}
|
||||
}
|
||||
|
||||
array := make([]uint16, 0)
|
||||
for _, tc := range internal.UnsignedIntegerTestCases {
|
||||
if tc.Val > math.MaxUint16 {
|
||||
continue
|
||||
}
|
||||
array = append(array, uint16(tc.Val))
|
||||
}
|
||||
|
||||
doOne(array)
|
||||
doOne(array[:1]) // single element
|
||||
doOne(array[:0]) // edge case of zero length
|
||||
}
|
||||
func TestAppendInts32(t *testing.T) {
|
||||
doOne := func(vals []int32) {
|
||||
want := make([]byte, 0)
|
||||
want = append(want, '[')
|
||||
for i, val := range vals {
|
||||
want = append(want, []byte(fmt.Sprintf("%d", int32(val)))...)
|
||||
if i < len(vals)-1 {
|
||||
want = append(want, ',')
|
||||
}
|
||||
}
|
||||
want = append(want, ']')
|
||||
|
||||
got := enc.AppendInts32([]byte{}, vals)
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("AppendInts32(%v)\ngot: %s\nwant: %s",
|
||||
vals,
|
||||
string(got),
|
||||
string(want))
|
||||
}
|
||||
}
|
||||
|
||||
array := make([]int32, 0)
|
||||
for _, tc := range internal.IntegerTestCases {
|
||||
if (tc.Val < math.MinInt32) || (tc.Val > math.MaxInt32) {
|
||||
continue
|
||||
}
|
||||
array = append(array, int32(tc.Val))
|
||||
}
|
||||
|
||||
doOne(array)
|
||||
doOne(array[:1]) // single element
|
||||
doOne(array[:0]) // edge case of zero length
|
||||
}
|
||||
func TestAppendUints32(t *testing.T) {
|
||||
doOne := func(vals []uint32) {
|
||||
want := make([]byte, 0)
|
||||
want = append(want, '[')
|
||||
for i, val := range vals {
|
||||
want = append(want, []byte(fmt.Sprintf("%d", uint32(val)))...)
|
||||
if i < len(vals)-1 {
|
||||
want = append(want, ',')
|
||||
}
|
||||
}
|
||||
want = append(want, ']')
|
||||
|
||||
got := enc.AppendUints32([]byte{}, vals)
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("AppendUints32(%v)\ngot: %s\nwant: %s",
|
||||
vals,
|
||||
string(got),
|
||||
string(want))
|
||||
}
|
||||
}
|
||||
|
||||
array := make([]uint32, 0)
|
||||
for _, tc := range internal.UnsignedIntegerTestCases {
|
||||
if tc.Val > math.MaxUint32 {
|
||||
continue
|
||||
}
|
||||
array = append(array, uint32(tc.Val))
|
||||
}
|
||||
|
||||
doOne(array)
|
||||
doOne(array[:1]) // single element
|
||||
doOne(array[:0]) // edge case of zero length
|
||||
}
|
||||
|
||||
func TestAppendInt64(t *testing.T) {
|
||||
doOne := func(vals []int64) {
|
||||
want := make([]byte, 0)
|
||||
want = append(want, '[')
|
||||
for i, val := range vals {
|
||||
want = append(want, []byte(fmt.Sprintf("%d", int64(val)))...)
|
||||
if i < len(vals)-1 {
|
||||
want = append(want, ',')
|
||||
}
|
||||
}
|
||||
want = append(want, ']')
|
||||
|
||||
got := enc.AppendInts64([]byte{}, vals)
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("AppendInts64(%v)\ngot: %s\nwant: %s",
|
||||
vals,
|
||||
string(got),
|
||||
string(want))
|
||||
}
|
||||
}
|
||||
|
||||
array := make([]int64, 0)
|
||||
for _, tc := range internal.IntegerTestCases {
|
||||
array = append(array, int64(tc.Val))
|
||||
}
|
||||
|
||||
doOne(array)
|
||||
doOne(array[:1]) // single element
|
||||
doOne(array[:0]) // edge case of zero length
|
||||
}
|
||||
func TestAppendUints64(t *testing.T) {
|
||||
doOne := func(vals []uint64) {
|
||||
want := make([]byte, 0)
|
||||
want = append(want, '[')
|
||||
for i, val := range vals {
|
||||
want = append(want, []byte(fmt.Sprintf("%d", uint64(val)))...)
|
||||
if i < len(vals)-1 {
|
||||
want = append(want, ',')
|
||||
}
|
||||
}
|
||||
want = append(want, ']')
|
||||
|
||||
got := enc.AppendUints64([]byte{}, vals)
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("AppendUints64(%v)\ngot: %s\nwant: %s",
|
||||
vals,
|
||||
string(got),
|
||||
string(want))
|
||||
}
|
||||
}
|
||||
|
||||
array := make([]uint64, 0)
|
||||
for _, tc := range internal.UnsignedIntegerTestCases {
|
||||
array = append(array, uint64(tc.Val))
|
||||
}
|
||||
|
||||
doOne(array)
|
||||
doOne(array[:1]) // single element
|
||||
doOne(array[:0]) // edge case of zero length
|
||||
}
|
||||
|
||||
func TestAppendInt(t *testing.T) {
|
||||
for _, tc := range internal.IntegerTestCases {
|
||||
want := []byte(fmt.Sprintf("%d", tc.Val))
|
||||
got := enc.AppendInt([]byte{}, tc.Val)
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("AppendInt(0x%x)\ngot: %s\nwant: %s",
|
||||
tc.Val,
|
||||
string(got),
|
||||
string(want))
|
||||
}
|
||||
}
|
||||
}
|
||||
func TestAppendUint(t *testing.T) {
|
||||
for _, tc := range internal.UnsignedIntegerTestCases {
|
||||
want := []byte(fmt.Sprintf("%d", tc.Val))
|
||||
got := enc.AppendUint([]byte{}, tc.Val)
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("AppendUint(0x%x)\ngot: %s\nwant: %s",
|
||||
tc.Val,
|
||||
string(got),
|
||||
string(want))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppendInts(t *testing.T) {
|
||||
doOne := func(vals []int) {
|
||||
want := make([]byte, 0)
|
||||
want = append(want, '[')
|
||||
for i, val := range vals {
|
||||
want = append(want, []byte(fmt.Sprintf("%d", int(val)))...)
|
||||
if i < len(vals)-1 {
|
||||
want = append(want, ',')
|
||||
}
|
||||
}
|
||||
want = append(want, ']')
|
||||
|
||||
got := enc.AppendInts([]byte{}, vals)
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("AppendInts(%v)\ngot: %s\nwant: %s",
|
||||
vals,
|
||||
string(got),
|
||||
string(want))
|
||||
}
|
||||
}
|
||||
|
||||
array := make([]int, 0)
|
||||
for _, tc := range internal.IntegerTestCases {
|
||||
array = append(array, int(tc.Val))
|
||||
}
|
||||
|
||||
doOne(array)
|
||||
doOne(array[:1]) // single element
|
||||
doOne(array[:0]) // edge case of zero length
|
||||
}
|
||||
func TestAppendUints(t *testing.T) {
|
||||
doOne := func(vals []uint) {
|
||||
want := make([]byte, 0)
|
||||
want = append(want, '[')
|
||||
for i, val := range vals {
|
||||
want = append(want, []byte(fmt.Sprintf("%d", uint(val)))...)
|
||||
if i < len(vals)-1 {
|
||||
want = append(want, ',')
|
||||
}
|
||||
}
|
||||
want = append(want, ']')
|
||||
|
||||
got := enc.AppendUints([]byte{}, vals)
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("AppendUints(%v)\ngot: %s\nwant: %s",
|
||||
vals,
|
||||
string(got),
|
||||
string(want))
|
||||
}
|
||||
}
|
||||
|
||||
array := make([]uint, 0)
|
||||
for _, tc := range internal.UnsignedIntegerTestCases {
|
||||
array = append(array, uint(tc.Val))
|
||||
}
|
||||
|
||||
doOne(array)
|
||||
doOne(array[:1]) // single element
|
||||
doOne(array[:0]) // edge case of zero length
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
const hex = "0123456789abcdef"
|
||||
const hexCharacters = "0123456789abcdef"
|
||||
|
||||
var noEscapeTable = [256]bool{}
|
||||
|
||||
@@ -137,7 +137,7 @@ func appendStringComplex(dst []byte, s string, i int) []byte {
|
||||
case '\t':
|
||||
dst = append(dst, '\\', 't')
|
||||
default:
|
||||
dst = append(dst, '\\', 'u', '0', '0', hex[b>>4], hex[b&0xF])
|
||||
dst = append(dst, '\\', 'u', '0', '0', hexCharacters[b>>4], hexCharacters[b&0xF])
|
||||
}
|
||||
i++
|
||||
start = i
|
||||
|
||||
@@ -2,72 +2,51 @@ package json
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/rs/zerolog/internal"
|
||||
)
|
||||
|
||||
var encodeStringTests = []struct {
|
||||
in string
|
||||
out string
|
||||
}{
|
||||
{"", `""`},
|
||||
{"\\", `"\\"`},
|
||||
{"\x00", `"\u0000"`},
|
||||
{"\x01", `"\u0001"`},
|
||||
{"\x02", `"\u0002"`},
|
||||
{"\x03", `"\u0003"`},
|
||||
{"\x04", `"\u0004"`},
|
||||
{"\x05", `"\u0005"`},
|
||||
{"\x06", `"\u0006"`},
|
||||
{"\x07", `"\u0007"`},
|
||||
{"\x08", `"\b"`},
|
||||
{"\x09", `"\t"`},
|
||||
{"\x0a", `"\n"`},
|
||||
{"\x0b", `"\u000b"`},
|
||||
{"\x0c", `"\f"`},
|
||||
{"\x0d", `"\r"`},
|
||||
{"\x0e", `"\u000e"`},
|
||||
{"\x0f", `"\u000f"`},
|
||||
{"\x10", `"\u0010"`},
|
||||
{"\x11", `"\u0011"`},
|
||||
{"\x12", `"\u0012"`},
|
||||
{"\x13", `"\u0013"`},
|
||||
{"\x14", `"\u0014"`},
|
||||
{"\x15", `"\u0015"`},
|
||||
{"\x16", `"\u0016"`},
|
||||
{"\x17", `"\u0017"`},
|
||||
{"\x18", `"\u0018"`},
|
||||
{"\x19", `"\u0019"`},
|
||||
{"\x1a", `"\u001a"`},
|
||||
{"\x1b", `"\u001b"`},
|
||||
{"\x1c", `"\u001c"`},
|
||||
{"\x1d", `"\u001d"`},
|
||||
{"\x1e", `"\u001e"`},
|
||||
{"\x1f", `"\u001f"`},
|
||||
{"✭", `"✭"`},
|
||||
{"foo\xc2\x7fbar", `"foo\ufffd\u007fbar"`}, // invalid sequence
|
||||
{"ascii", `"ascii"`},
|
||||
{"\"a", `"\"a"`},
|
||||
{"\x1fa", `"\u001fa"`},
|
||||
{"foo\"bar\"baz", `"foo\"bar\"baz"`},
|
||||
{"\x1ffoo\x1fbar\x1fbaz", `"\u001ffoo\u001fbar\u001fbaz"`},
|
||||
{"emoji \u2764\ufe0f!", `"emoji ❤️!"`},
|
||||
}
|
||||
|
||||
var encodeHexTests = []struct {
|
||||
in byte
|
||||
out string
|
||||
}{
|
||||
{0x00, `"00"`},
|
||||
{0x0f, `"0f"`},
|
||||
{0x10, `"10"`},
|
||||
{0xf0, `"f0"`},
|
||||
{0xff, `"ff"`},
|
||||
}
|
||||
|
||||
func TestAppendString(t *testing.T) {
|
||||
for _, tt := range encodeStringTests {
|
||||
b := enc.AppendString([]byte{}, tt.in)
|
||||
if got, want := string(b), tt.out; got != want {
|
||||
t.Errorf("appendString(%q) = %#q, want %#q", tt.in, got, want)
|
||||
for _, tt := range internal.EncodeStringTests {
|
||||
b := enc.AppendString([]byte{}, tt.In)
|
||||
if got, want := string(b), tt.Out; got != want {
|
||||
t.Errorf("appendString(%q) = %#q, want %#q", tt.In, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppendStrings(t *testing.T) {
|
||||
for _, tt := range internal.EncodeStringsTests {
|
||||
b := enc.AppendStrings([]byte{}, tt.In)
|
||||
if got, want := string(b), tt.Out; got != want {
|
||||
t.Errorf("appendStrings(%q) = %#q, want %#q", tt.In, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppendStringer(t *testing.T) {
|
||||
oldJSONMarshalFunc := JSONMarshalFunc
|
||||
defer func() {
|
||||
JSONMarshalFunc = oldJSONMarshalFunc
|
||||
}()
|
||||
|
||||
JSONMarshalFunc = func(v interface{}) ([]byte, error) {
|
||||
return internal.InterfaceMarshalFunc(v)
|
||||
}
|
||||
|
||||
for _, tt := range internal.EncodeStringerTests {
|
||||
b := enc.AppendStringer([]byte{}, tt.In)
|
||||
if got, want := string(b), tt.Out; got != want {
|
||||
t.Errorf("AppendStringer(%q)\ngot: %#q, want: %#q", tt.In, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppendStringers(t *testing.T) {
|
||||
for _, tt := range internal.EncodeStringersTests {
|
||||
b := enc.AppendStringers([]byte{}, tt.In)
|
||||
if got, want := string(b), tt.Out; got != want {
|
||||
t.Errorf("appendStrings(%q) = %#q, want %#q", tt.In, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,234 @@
|
||||
package json
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog/internal"
|
||||
)
|
||||
|
||||
func TestAppendTimeNow(t *testing.T) {
|
||||
tm := time.Now()
|
||||
got := enc.AppendTime([]byte{}, tm, time.RFC3339)
|
||||
want := tm.AppendFormat([]byte{'"'}, time.RFC3339)
|
||||
want = append(want, '"')
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("AppendTime(%s)\ngot: %s\nwant: %s",
|
||||
"time.Now()",
|
||||
string(got),
|
||||
string(want))
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppendTimePastPresentInteger(t *testing.T) {
|
||||
for _, tt := range internal.TimeIntegerTestcases {
|
||||
tin, err := time.Parse(time.RFC3339, tt.Txt)
|
||||
if err != nil {
|
||||
fmt.Println("Cannot parse input", tt.Txt, ".. Skipping!", err)
|
||||
continue
|
||||
}
|
||||
|
||||
got := enc.AppendTime([]byte{}, tin, timeFormatUnix)
|
||||
want := []byte(fmt.Sprintf("%d", tt.UnixInt))
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("appendString(%s)\ngot: %s\nwant: %s",
|
||||
tt.Txt,
|
||||
string(got),
|
||||
string(want))
|
||||
}
|
||||
got = enc.AppendTime([]byte{}, tin, timeFormatUnixMs)
|
||||
want = []byte(fmt.Sprintf("%d", tt.UnixInt*1000))
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("appendString(%s)\ngot: %s\nwant: %s",
|
||||
tt.Txt,
|
||||
string(got),
|
||||
string(want))
|
||||
}
|
||||
got = enc.AppendTime([]byte{}, tin, timeFormatUnixMicro)
|
||||
want = []byte(fmt.Sprintf("%d", tt.UnixInt*1000000))
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("appendString(%s)\ngot: %s\nwant: %s",
|
||||
tt.Txt,
|
||||
string(got),
|
||||
string(want))
|
||||
}
|
||||
got = enc.AppendTime([]byte{}, tin, timeFormatUnixNano)
|
||||
want = []byte(fmt.Sprintf("%d", tt.UnixInt*1000000000))
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("appendString(%s)\ngot: %s\nwant: %s",
|
||||
tt.Txt,
|
||||
string(got),
|
||||
string(want))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppendTimePastPresentFloat(t *testing.T) {
|
||||
const timeFloatFmt = "2006-01-02T15:04:05.999999-07:00"
|
||||
for _, tt := range internal.TimeFloatTestcases {
|
||||
tin, err := time.Parse(timeFloatFmt, tt.RfcStr)
|
||||
if err != nil {
|
||||
fmt.Println("Cannot parse input", tt.RfcStr, ".. Skipping!")
|
||||
continue
|
||||
}
|
||||
got := enc.AppendTime([]byte{}, tin, timeFormatUnix)
|
||||
want := []byte(fmt.Sprintf("%d", tt.UnixInt))
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("appendString(%s)\ngot: %s\nwant: %s",
|
||||
tt.RfcStr,
|
||||
string(got),
|
||||
string(want))
|
||||
}
|
||||
}
|
||||
}
|
||||
func TestAppendTimes(t *testing.T) {
|
||||
doOne := func(multiplier int, format string) {
|
||||
array := make([]time.Time, 0)
|
||||
want := append([]byte{}, '[')
|
||||
want = append(want, ']')
|
||||
got := enc.AppendTimes([]byte{}, array, format)
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("AppendTimes(%v)\ngot: %s\nwant: %s",
|
||||
array,
|
||||
string(got),
|
||||
string(want))
|
||||
}
|
||||
|
||||
array = make([]time.Time, len(internal.TimeIntegerTestcases))
|
||||
want = append([]byte{}, '[')
|
||||
for i, tt := range internal.TimeIntegerTestcases {
|
||||
if tin, err := time.Parse(time.RFC3339, tt.RfcStr); err != nil {
|
||||
fmt.Println("Cannot parse input", tt.RfcStr, ".. Skipping!")
|
||||
continue
|
||||
} else {
|
||||
array[i] = tin
|
||||
}
|
||||
if multiplier == 0 {
|
||||
want = append(want, '"')
|
||||
formatted := array[i].Format(format)
|
||||
want = append(want, []byte(fmt.Sprintf("%v", formatted))...)
|
||||
want = append(want, '"')
|
||||
} else {
|
||||
scaled := tt.UnixInt * multiplier
|
||||
want = append(want, []byte(fmt.Sprintf("%d", scaled))...)
|
||||
}
|
||||
if i < len(internal.TimeIntegerTestcases)-1 {
|
||||
want = append(want, ',')
|
||||
}
|
||||
}
|
||||
want = append(want, ']')
|
||||
|
||||
got = enc.AppendTimes([]byte{}, array, format)
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("AppendTimes(%v) %d %s\ngot: %s\nwant: %s",
|
||||
array, multiplier, format,
|
||||
string(got),
|
||||
string(want))
|
||||
}
|
||||
}
|
||||
|
||||
doOne(0, time.RFC3339)
|
||||
doOne(1, timeFormatUnix)
|
||||
doOne(1000, timeFormatUnixMs)
|
||||
doOne(1000000, timeFormatUnixMicro)
|
||||
doOne(1000000000, timeFormatUnixNano)
|
||||
}
|
||||
|
||||
func TestAppendDurationFloat(t *testing.T) {
|
||||
for _, tt := range internal.DurTestcases {
|
||||
dur := tt.Duration
|
||||
want := []byte{}
|
||||
want = append(want, []byte(fmt.Sprintf("%v", dur.Microseconds()))...)
|
||||
got := enc.AppendDuration([]byte{}, dur, time.Microsecond, false, -1)
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("AppendDuration(%v)=\ngot: %s\nwant: %s",
|
||||
dur,
|
||||
string(got),
|
||||
string(want))
|
||||
}
|
||||
|
||||
want = []byte{}
|
||||
fraction := float64(dur) / float64(time.Millisecond)
|
||||
want = append(want, []byte(fmt.Sprintf("%v", fraction))...)
|
||||
got = enc.AppendDuration([]byte{}, dur, time.Millisecond, false, -1)
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("AppendDuration(%v)=\ngot: %s\nwant: %s",
|
||||
dur,
|
||||
string(got),
|
||||
string(want))
|
||||
}
|
||||
}
|
||||
}
|
||||
func TestAppendDurationInteger(t *testing.T) {
|
||||
for _, tt := range internal.DurTestcases {
|
||||
dur := tt.Duration
|
||||
want := []byte{}
|
||||
whole := int(dur) / int(time.Microsecond)
|
||||
want = append(want, []byte(fmt.Sprintf("%v", whole))...)
|
||||
got := enc.AppendDuration([]byte{}, dur, time.Microsecond, true, -1)
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("AppendDuration(%v)=\ngot: %s\nwant: %s",
|
||||
dur,
|
||||
string(got),
|
||||
string(want))
|
||||
}
|
||||
}
|
||||
}
|
||||
func TestAppendDurations(t *testing.T) {
|
||||
array := make([]time.Duration, len(internal.DurTestcases))
|
||||
want := make([]byte, 0)
|
||||
want = append(want, '[')
|
||||
for i, tt := range internal.DurTestcases {
|
||||
array[i] = tt.Duration
|
||||
whole := int(tt.Duration) / int(time.Microsecond)
|
||||
want = append(want, []byte(fmt.Sprintf("%v", whole))...)
|
||||
if i < len(internal.DurTestcases)-1 {
|
||||
want = append(want, ',')
|
||||
}
|
||||
}
|
||||
want = append(want, ']')
|
||||
|
||||
got := enc.AppendDurations([]byte{}, array, time.Microsecond, false, -1)
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("AppendDurations(%v)\ngot: %s\nwant: %s",
|
||||
array,
|
||||
string(got),
|
||||
string(want))
|
||||
}
|
||||
|
||||
// now empty array case
|
||||
array = make([]time.Duration, 0)
|
||||
want = make([]byte, 0)
|
||||
want = append(want, '[')
|
||||
want = append(want, ']')
|
||||
got = enc.AppendDurations([]byte{}, array, time.Microsecond, false, -1)
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("AppendDurations(%v)\ngot: %s\nwant: %s",
|
||||
array,
|
||||
string(got),
|
||||
string(want))
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAppendTime(b *testing.B) {
|
||||
tests := map[string]string{
|
||||
"Integer": "Feb 3, 2013 at 7:54pm (PST)",
|
||||
"Float": "2006-01-02T15:04:05.999999-08:00",
|
||||
}
|
||||
const timeFloatFmt = "2006-01-02T15:04:05.999999-07:00"
|
||||
|
||||
for name, str := range tests {
|
||||
t, err := time.Parse(time.RFC3339, str)
|
||||
if err != nil {
|
||||
t, _ = time.Parse(timeFloatFmt, str)
|
||||
}
|
||||
b.Run(name, func(b *testing.B) {
|
||||
buf := make([]byte, 0, 100)
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = enc.AppendTime(buf, t, "unused")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
+199
-346
@@ -1,14 +1,141 @@
|
||||
package json
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"math"
|
||||
"math/rand"
|
||||
"net"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/rs/zerolog/internal"
|
||||
)
|
||||
|
||||
func TestAppendNil(t *testing.T) {
|
||||
got := enc.AppendNil([]byte{})
|
||||
want := []byte(`null`)
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("AppendNil() = %s, want: %s",
|
||||
string(got),
|
||||
string(want))
|
||||
}
|
||||
}
|
||||
func TestAppendBeginMarker(t *testing.T) {
|
||||
got := enc.AppendBeginMarker([]byte{})
|
||||
want := []byte(`{`)
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("AppendBeginMarker()\ngot: %s, want: %s",
|
||||
string(got),
|
||||
string(want))
|
||||
}
|
||||
}
|
||||
func TestAppendEndMarker(t *testing.T) {
|
||||
got := enc.AppendEndMarker([]byte{})
|
||||
want := []byte(`}`)
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("AppendEndMarker()\ngot: %s, want: %s",
|
||||
string(got),
|
||||
string(want))
|
||||
}
|
||||
}
|
||||
func TestAppendArrayStart(t *testing.T) {
|
||||
got := enc.AppendArrayStart([]byte{})
|
||||
want := []byte("[")
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("AppendArrayStart() = %s, want: %s",
|
||||
string(got),
|
||||
string(want))
|
||||
}
|
||||
}
|
||||
func TestAppendArrayEnd(t *testing.T) {
|
||||
got := enc.AppendArrayEnd([]byte{})
|
||||
want := []byte("]")
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("AppendArrayEnd() = %s, want: %s",
|
||||
string(got),
|
||||
string(want))
|
||||
}
|
||||
}
|
||||
func TestAppendArrayDelim(t *testing.T) {
|
||||
got := enc.AppendArrayDelim([]byte{})
|
||||
want := []byte("")
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("AppendArrayDelim() = 0x%s, want: 0x%s",
|
||||
string(got),
|
||||
string(want))
|
||||
}
|
||||
|
||||
got = enc.AppendArrayDelim([]byte("a"))
|
||||
want = []byte("a,")
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("AppendArrayDelim() = 0x%s, want: 0x%s",
|
||||
string(got),
|
||||
string(want))
|
||||
}
|
||||
}
|
||||
func TestAppendLineBreak(t *testing.T) {
|
||||
got := enc.AppendLineBreak([]byte{})
|
||||
want := []byte("\n")
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("AppendLineBreak() = 0x%s, want: 0x%s",
|
||||
string(got),
|
||||
string(want))
|
||||
}
|
||||
}
|
||||
|
||||
// inline copy from globals.go of InterfaceMarshalFunc used in tests to avoid import cycle
|
||||
func interfaceMarshalFunc(v interface{}) ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
encoder := json.NewEncoder(&buf)
|
||||
encoder.SetEscapeHTML(false)
|
||||
err := encoder.Encode(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b := buf.Bytes()
|
||||
if len(b) > 0 {
|
||||
// Remove trailing \n which is added by Encode.
|
||||
return b[:len(b)-1], nil
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func TestAppendInterface(t *testing.T) {
|
||||
oldJSONMarshalFunc := JSONMarshalFunc
|
||||
defer func() {
|
||||
JSONMarshalFunc = oldJSONMarshalFunc
|
||||
}()
|
||||
|
||||
JSONMarshalFunc = func(v interface{}) ([]byte, error) {
|
||||
return interfaceMarshalFunc(v)
|
||||
}
|
||||
|
||||
var i int = 17
|
||||
got := enc.AppendInterface([]byte{}, i)
|
||||
want := make([]byte, 0)
|
||||
want = append(want, []byte("17")...) // of type interface, two characters
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("AppendInterface\ngot: 0x%s\nwant: 0x%s",
|
||||
string(got),
|
||||
string(want))
|
||||
}
|
||||
|
||||
JSONMarshalFunc = func(v interface{}) ([]byte, error) {
|
||||
return nil, errors.New("test")
|
||||
}
|
||||
|
||||
got = enc.AppendInterface([]byte{}, nil)
|
||||
want = make([]byte, 0)
|
||||
want = append(want, []byte("\"marshaling error: test\"")...) // of type interface, two characters
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("AppendInterface\ngot: 0x%s\nwant: 0x%s",
|
||||
string(got),
|
||||
string(want))
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppendType(t *testing.T) {
|
||||
w := map[string]func(interface{}) []byte{
|
||||
"AppendInt": func(v interface{}) []byte { return enc.AppendInt([]byte{}, v.(int)) },
|
||||
@@ -70,7 +197,7 @@ func TestAppendType(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func Test_appendMAC(t *testing.T) {
|
||||
func TestAppendMAC(t *testing.T) {
|
||||
MACtests := []struct {
|
||||
input string
|
||||
want []byte
|
||||
@@ -88,7 +215,70 @@ func Test_appendMAC(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func Test_appendIP(t *testing.T) {
|
||||
func TestAppendBool(t *testing.T) {
|
||||
for _, tc := range internal.BooleanTestCases {
|
||||
s := enc.AppendBool([]byte{}, tc.Val)
|
||||
got := string(s)
|
||||
if got != tc.Json {
|
||||
t.Errorf("AppendBool(%s)=0x%s, want: 0x%s",
|
||||
tc.Json,
|
||||
string(s),
|
||||
string([]byte(tc.Binary)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppendBoolArray(t *testing.T) {
|
||||
for _, tc := range internal.BooleanArrayTestCases {
|
||||
s := enc.AppendBools([]byte{}, tc.Val)
|
||||
got := string(s)
|
||||
if got != tc.Json {
|
||||
t.Errorf("AppendBools(%s)=0x%s, want: 0x%s",
|
||||
tc.Json,
|
||||
hex.EncodeToString(s),
|
||||
hex.EncodeToString([]byte(tc.Binary)))
|
||||
}
|
||||
}
|
||||
|
||||
// now empty array case
|
||||
array := make([]bool, 0)
|
||||
want := make([]byte, 0)
|
||||
want = append(want, []byte("[]")...) // start and end array
|
||||
got := enc.AppendBools([]byte{}, array)
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("AppendBools(%v)\ngot: 0x%s\nwant: 0x%s",
|
||||
array,
|
||||
hex.EncodeToString(got),
|
||||
hex.EncodeToString(want))
|
||||
}
|
||||
|
||||
// now a large array case
|
||||
array = make([]bool, 24)
|
||||
want = make([]byte, 0)
|
||||
want = append(want, []byte("[")...) // start a large array
|
||||
for i := 0; i < 24; i++ {
|
||||
array[i] = bool(i%2 == 1)
|
||||
if array[i] {
|
||||
want = append(want, []byte("true")...)
|
||||
} else {
|
||||
want = append(want, []byte("false")...)
|
||||
}
|
||||
if (i + 1) < 24 {
|
||||
want = append(want, []byte(",")...)
|
||||
}
|
||||
}
|
||||
want = append(want, []byte("]")...) // end a large array
|
||||
|
||||
got = enc.AppendBools([]byte{}, array)
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("AppendBools(%v)\ngot: 0x%s\nwant: 0x%s",
|
||||
array,
|
||||
string(got),
|
||||
string(want))
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppendIP(t *testing.T) {
|
||||
IPv4tests := []struct {
|
||||
input net.IP
|
||||
want []byte
|
||||
@@ -130,7 +320,7 @@ var IPAddrArrayTestCases = []struct {
|
||||
{[]net.IP{{0, 0, 0, 0}, {192, 168, 0, 100}}, []byte(`["0.0.0.0","192.168.0.100"]`)},
|
||||
}
|
||||
|
||||
func Test_appendIPAddr_array(t *testing.T) {
|
||||
func TestAppendIPAddrs(t *testing.T) {
|
||||
for _, tt := range IPAddrArrayTestCases {
|
||||
t.Run("IPAddrs", func(t *testing.T) {
|
||||
if got := enc.AppendIPAddrs([]byte{}, tt.input); !reflect.DeepEqual(got, tt.want) {
|
||||
@@ -149,7 +339,7 @@ var IPPrefixArrayTestCases = []struct {
|
||||
{[]net.IPNet{{IP: net.IP{0, 0, 0, 0}, Mask: net.CIDRMask(0, 32)}, {IP: net.IP{192, 168, 0, 100}, Mask: net.CIDRMask(24, 32)}}, []byte(`["0.0.0.0/0","192.168.0.100/24"]`)},
|
||||
}
|
||||
|
||||
func Test_appendIPPrefix_array(t *testing.T) {
|
||||
func TestAppendIPPrefixes(t *testing.T) {
|
||||
for _, tt := range IPPrefixArrayTestCases {
|
||||
t.Run("IPPrefixes", func(t *testing.T) {
|
||||
if got := enc.AppendIPPrefixes([]byte{}, tt.input); !reflect.DeepEqual(got, tt.want) {
|
||||
@@ -159,7 +349,7 @@ func Test_appendIPPrefix_array(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func Test_appendIPPrefix(t *testing.T) {
|
||||
func TestAppendIPPrefix(t *testing.T) {
|
||||
IPv4Prefixtests := []struct {
|
||||
input net.IPNet
|
||||
want []byte
|
||||
@@ -193,7 +383,7 @@ func Test_appendIPPrefix(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func Test_appendMac(t *testing.T) {
|
||||
func TestAppendMACAddr(t *testing.T) {
|
||||
MACtests := []struct {
|
||||
input net.HardwareAddr
|
||||
want []byte
|
||||
@@ -211,7 +401,7 @@ func Test_appendMac(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func Test_appendType(t *testing.T) {
|
||||
func TestAppendType2(t *testing.T) {
|
||||
typeTests := []struct {
|
||||
label string
|
||||
input interface{}
|
||||
@@ -233,7 +423,7 @@ func Test_appendType(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func Test_appendObjectData(t *testing.T) {
|
||||
func TestAppendObjectData(t *testing.T) {
|
||||
tests := []struct {
|
||||
dst []byte
|
||||
obj []byte
|
||||
@@ -252,340 +442,3 @@ func Test_appendObjectData(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,373 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
var BooleanTestCases = []struct {
|
||||
Val bool
|
||||
Binary string
|
||||
Json string
|
||||
}{
|
||||
{true, "\xf5", "true"},
|
||||
{false, "\xf4", "false"},
|
||||
}
|
||||
|
||||
var BooleanArrayTestCases = []struct {
|
||||
Val []bool
|
||||
Binary string
|
||||
Json string
|
||||
}{
|
||||
{[]bool{}, "\x9f\xff", "[]"},
|
||||
{[]bool{false}, "\x81\xf4", "[false]"},
|
||||
{[]bool{true, false, true}, "\x83\xf5\xf4\xf5", "[true,false,true]"},
|
||||
{[]bool{true, false, false, true, false, true}, "\x86\xf5\xf4\xf4\xf5\xf4\xf5", "[true,false,false,true,false,true]"},
|
||||
}
|
||||
|
||||
var IntegerTestCases = []struct {
|
||||
Val int
|
||||
Binary string
|
||||
}{
|
||||
// Value included in the type.
|
||||
{0, "\x00"},
|
||||
{1, "\x01"},
|
||||
{2, "\x02"},
|
||||
{3, "\x03"},
|
||||
{8, "\x08"},
|
||||
{9, "\x09"},
|
||||
{10, "\x0a"},
|
||||
{22, "\x16"},
|
||||
{23, "\x17"},
|
||||
|
||||
// Value in 1 byte.
|
||||
{24, "\x18\x18"},
|
||||
{25, "\x18\x19"},
|
||||
{26, "\x18\x1a"},
|
||||
{127, "\x18\x7f"},
|
||||
{254, "\x18\xfe"},
|
||||
{255, "\x18\xff"},
|
||||
|
||||
// Value in 2 bytes.
|
||||
{256, "\x19\x01\x00"},
|
||||
{257, "\x19\x01\x01"},
|
||||
{1000, "\x19\x03\xe8"},
|
||||
{0xFFFF, "\x19\xff\xff"},
|
||||
|
||||
// Value in 4 bytes.
|
||||
{0x10000, "\x1a\x00\x01\x00\x00"},
|
||||
{0x7FFFFFFE, "\x1a\x7f\xff\xff\xfe"},
|
||||
{1000000, "\x1a\x00\x0f\x42\x40"},
|
||||
|
||||
// Negative number test cases.
|
||||
// Value included in the type.
|
||||
{-1, "\x20"},
|
||||
{-2, "\x21"},
|
||||
{-3, "\x22"},
|
||||
{-10, "\x29"},
|
||||
{-21, "\x34"},
|
||||
{-22, "\x35"},
|
||||
{-23, "\x36"},
|
||||
{-24, "\x37"},
|
||||
|
||||
// Value in 1 byte.
|
||||
{-25, "\x38\x18"},
|
||||
{-26, "\x38\x19"},
|
||||
{-100, "\x38\x63"},
|
||||
{-128, "\x38\x7f"},
|
||||
{-254, "\x38\xfd"},
|
||||
{-255, "\x38\xfe"},
|
||||
{-256, "\x38\xff"},
|
||||
|
||||
// Value in 2 bytes.
|
||||
{-257, "\x39\x01\x00"},
|
||||
{-258, "\x39\x01\x01"},
|
||||
{-1000, "\x39\x03\xe7"},
|
||||
|
||||
// Value in 4 bytes.
|
||||
{-0x10001, "\x3a\x00\x01\x00\x00"},
|
||||
{-0x7FFFFFFE, "\x3a\x7f\xff\xff\xfd"},
|
||||
{-1000000, "\x3a\x00\x0f\x42\x3f"},
|
||||
|
||||
//Constants
|
||||
{math.MaxInt8, "\x18\x7f"},
|
||||
{math.MinInt8, "\x38\x7f"},
|
||||
{math.MaxInt16, "\x19\x7f\xff"},
|
||||
{math.MinInt16, "\x39\x7f\xff"},
|
||||
{math.MaxInt32, "\x1a\x7f\xff\xff\xff"},
|
||||
{math.MinInt32, "\x3a\x7f\xff\xff\xff"},
|
||||
{math.MaxInt64, "\x1b\x7f\xff\xff\xff\xff\xff\xff\xff"},
|
||||
{math.MinInt64, "\x3b\x7f\xff\xff\xff\xff\xff\xff\xff"},
|
||||
}
|
||||
|
||||
type UnsignedIntTestCase struct {
|
||||
Val uint
|
||||
Binary string
|
||||
Bigbinary string
|
||||
}
|
||||
|
||||
var AdditionalUnsignedIntegerTestCases = []UnsignedIntTestCase{
|
||||
{0x7FFFFFFF, "\x18\xff", "\x1a\x7f\xff\xff\xff"},
|
||||
{0x80000000, "\x19\xff\xff", "\x1a\x80\x00\x00\x00"},
|
||||
{1000000, "\x1b\x80\x00\x00\x00\x00\x00\x00\x00", "\x1a\x00\x0f\x42\x40"},
|
||||
|
||||
//Constants
|
||||
{math.MaxUint8, "\x18\xff", "\x18\xff"},
|
||||
{math.MaxUint16, "\x19\xff\xff", "\x19\xff\xff"},
|
||||
{math.MaxUint32, "\x1a\xff\xff\xff\xff", "\x1a\xff\xff\xff\xff"},
|
||||
{math.MaxUint64, "\x1b\xff\xff\xff\xff\xff\xff\xff\xff", "\x1b\xff\xff\xff\xff\xff\xff\xff\xff"},
|
||||
}
|
||||
|
||||
func unsignedIntegerTestCases() []UnsignedIntTestCase {
|
||||
size := len(IntegerTestCases) + len(AdditionalUnsignedIntegerTestCases)
|
||||
cases := make([]UnsignedIntTestCase, 0, size)
|
||||
cases = append(cases, AdditionalUnsignedIntegerTestCases...)
|
||||
for _, itc := range IntegerTestCases {
|
||||
if itc.Val < 0 {
|
||||
continue
|
||||
}
|
||||
cases = append(cases, UnsignedIntTestCase{Val: uint(itc.Val), Binary: itc.Binary, Bigbinary: itc.Binary})
|
||||
}
|
||||
return cases
|
||||
}
|
||||
|
||||
var UnsignedIntegerTestCases = unsignedIntegerTestCases()
|
||||
|
||||
var Float32TestCases = []struct {
|
||||
Val float32
|
||||
Binary string
|
||||
}{
|
||||
{0.0, "\xfa\x00\x00\x00\x00"},
|
||||
{-0.0, "\xfa\x00\x00\x00\x00"},
|
||||
{1.0, "\xfa\x3f\x80\x00\x00"},
|
||||
{1.5, "\xfa\x3f\xc0\x00\x00"},
|
||||
{65504.0, "\xfa\x47\x7f\xe0\x00"},
|
||||
{-4.0, "\xfa\xc0\x80\x00\x00"},
|
||||
{0.00006103515625, "\xfa\x38\x80\x00\x00"},
|
||||
{float32(math.Inf(0)), "\xfa\x7f\x80\x00\x00"},
|
||||
{float32(math.Inf(-1)), "\xfa\xff\x80\x00\x00"},
|
||||
{float32(math.NaN()), "\xfa\x7f\xc0\x00\x00"},
|
||||
{math.SmallestNonzeroFloat32, "\xfa\x00\x00\x00\x01"},
|
||||
{math.MaxFloat32, "\xfa\x7f\x7f\xff\xff"},
|
||||
}
|
||||
|
||||
var Float64TestCases = []struct {
|
||||
Val float64
|
||||
Binary string
|
||||
}{
|
||||
{0.0, "\xfa\x00\x00\x00\x00"},
|
||||
{-0.0, "\xfa\x00\x00\x00\x00"},
|
||||
{1.0, "\xfa\x3f\x80\x00\x00"},
|
||||
{1.5, "\xfa\x3f\xc0\x00\x00"},
|
||||
{65504.0, "\xfa\x47\x7f\xe0\x00"},
|
||||
{-4.0, "\xfa\xc0\x80\x00\x00"},
|
||||
{0.00006103515625, "\xfa\x38\x80\x00\x00"},
|
||||
{math.Inf(0), "\xfa\x7f\x80\x00\x00\x00\x00\x00\x00"},
|
||||
{math.Inf(-1), "\xfa\xff\x80\x00\x00\x00\x00\x00\x00"},
|
||||
{math.NaN(), "\xfb\x7f\xf8\x00\x00\x00\x00\x00\x00"},
|
||||
{math.SmallestNonzeroFloat64, "\xfa\x00\x00\x00\x00\x00\x00\x00\x01"},
|
||||
{math.MaxFloat64, "\xfa\x7f\x7f\xff\xff"},
|
||||
}
|
||||
|
||||
var IntegerArrayTestCases = []struct {
|
||||
Val []int
|
||||
Binary string
|
||||
Json string
|
||||
}{
|
||||
{[]int{}, "\x9f\xff", "[]"},
|
||||
{[]int{32768}, "\x81\x19\x80\x00", "[32768]"},
|
||||
{[]int{-1, 0, 200, 20}, "\x84\x20\x00\x18\xc8\x14", "[-1,0,200,20]"},
|
||||
{[]int{-200, -10, 200, 400}, "\x84\x38\xc7\x29\x18\xc8\x19\x01\x90", "[-200,-10,200,400]"},
|
||||
{[]int{1, 2, 3}, "\x83\x01\x02\x03", "[1,2,3]"},
|
||||
{[]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25},
|
||||
"\x98\x19\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x18\x18\x19",
|
||||
"[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25]"},
|
||||
}
|
||||
|
||||
var IpAddrTestCases = []struct {
|
||||
Ipaddr net.IP
|
||||
Text string
|
||||
Binary string
|
||||
}{
|
||||
{net.IP{10, 0, 0, 1}, "\"10.0.0.1\"", "\xd9\x01\x04\x44\x0a\x00\x00\x01"},
|
||||
{net.IP{0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x0, 0x0, 0x0, 0x0, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x34},
|
||||
"\"2001:db8:85a3::8a2e:370:7334\"",
|
||||
"\xd9\x01\x04\x50\x20\x01\x0d\xb8\x85\xa3\x00\x00\x00\x00\x8a\x2e\x03\x70\x73\x34"},
|
||||
}
|
||||
|
||||
var IPAddrArrayTestCases = []struct {
|
||||
Val []net.IP
|
||||
Binary string
|
||||
Json string
|
||||
}{
|
||||
{[]net.IP{}, "\x9f\xff", "[]"},
|
||||
{[]net.IP{{127, 0, 0, 0}}, "\x81\xd9\x01\x04\x44\x7f\x00\x00\x00", "[127.0.0.0]"},
|
||||
{[]net.IP{{0, 0, 0, 0}, {192, 168, 0, 100}}, "\x82\xd9\x01\x04\x44\x00\x00\x00\x00\xd9\x01\x04\x44\xc0\xa8\x00\x64", "[0.0.0.0,192.168.0.100]"},
|
||||
}
|
||||
|
||||
var IPPrefixTestCases = []struct {
|
||||
Pfx net.IPNet
|
||||
Text string // ASCII representation of pfx
|
||||
Binary string // CBOR representation of pfx
|
||||
}{
|
||||
{net.IPNet{IP: net.IP{0, 0, 0, 0}, Mask: net.CIDRMask(0, 32)}, "\"0.0.0.0/0\"", "\xd9\x01\x05\xa1\x44\x00\x00\x00\x00\x00"},
|
||||
{net.IPNet{IP: net.IP{192, 168, 0, 100}, Mask: net.CIDRMask(24, 32)}, "\"192.168.0.100/24\"",
|
||||
"\xd9\x01\x05\xa1\x44\xc0\xa8\x00\x64\x18\x18"},
|
||||
}
|
||||
|
||||
var IPPrefixArrayTestCases = []struct {
|
||||
Val []net.IPNet
|
||||
Binary string
|
||||
Json string
|
||||
}{
|
||||
{[]net.IPNet{}, "\x9f\xff", "[]"},
|
||||
{[]net.IPNet{{IP: net.IP{127, 0, 0, 0}, Mask: net.CIDRMask(24, 32)}}, "\x81\xd9\x01\x05\xa1\x44\x7f\x00\x00\x00\x18\x18", "[127.0.0.0/24]"},
|
||||
{[]net.IPNet{{IP: net.IP{0, 0, 0, 0}, Mask: net.CIDRMask(0, 32)}, {IP: net.IP{192, 168, 0, 100}, Mask: net.CIDRMask(24, 32)}}, "\x82\xd9\x01\x05\xa1\x44\x00\x00\x00\x00\x00\xd9\x01\x05\xa1\x44\xc0\xa8\x00\x64\x18\x18", "[0.0.0.0/0,192.168.0.100/24]"},
|
||||
}
|
||||
|
||||
var MacAddrTestCases = []struct {
|
||||
Macaddr net.HardwareAddr
|
||||
Text string // ASCII representation of macaddr
|
||||
Binary string // CBOR representation of macaddr
|
||||
}{
|
||||
{net.HardwareAddr{0x12, 0x34, 0x56, 0x78, 0x90, 0xab}, "\"12:34:56:78:90:ab\"", "\xd9\x01\x04\x46\x12\x34\x56\x78\x90\xab"},
|
||||
{net.HardwareAddr{0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3}, "\"20:01:0d:b8:85:a3\"", "\xd9\x01\x04\x46\x20\x01\x0d\xb8\x85\xa3"},
|
||||
}
|
||||
|
||||
var EncodeHexTests = []struct {
|
||||
In byte
|
||||
Out string
|
||||
}{
|
||||
{0x00, `"00"`},
|
||||
{0x0f, `"0f"`},
|
||||
{0x10, `"10"`},
|
||||
{0xf0, `"f0"`},
|
||||
{0xff, `"ff"`},
|
||||
}
|
||||
|
||||
var EncodeStringTests = []struct {
|
||||
In string
|
||||
Out string
|
||||
}{
|
||||
{"", `""`},
|
||||
{"\\", `"\\"`},
|
||||
{"\x00", `"\u0000"`},
|
||||
{"\x01", `"\u0001"`},
|
||||
{"\x02", `"\u0002"`},
|
||||
{"\x03", `"\u0003"`},
|
||||
{"\x04", `"\u0004"`},
|
||||
{"\x05", `"\u0005"`},
|
||||
{"\x06", `"\u0006"`},
|
||||
{"\x07", `"\u0007"`},
|
||||
{"\x08", `"\b"`},
|
||||
{"\x09", `"\t"`},
|
||||
{"\x0a", `"\n"`},
|
||||
{"\x0b", `"\u000b"`},
|
||||
{"\x0c", `"\f"`},
|
||||
{"\x0d", `"\r"`},
|
||||
{"\x0e", `"\u000e"`},
|
||||
{"\x0f", `"\u000f"`},
|
||||
{"\x10", `"\u0010"`},
|
||||
{"\x11", `"\u0011"`},
|
||||
{"\x12", `"\u0012"`},
|
||||
{"\x13", `"\u0013"`},
|
||||
{"\x14", `"\u0014"`},
|
||||
{"\x15", `"\u0015"`},
|
||||
{"\x16", `"\u0016"`},
|
||||
{"\x17", `"\u0017"`},
|
||||
{"\x18", `"\u0018"`},
|
||||
{"\x19", `"\u0019"`},
|
||||
{"\x1a", `"\u001a"`},
|
||||
{"\x1b", `"\u001b"`},
|
||||
{"\x1c", `"\u001c"`},
|
||||
{"\x1d", `"\u001d"`},
|
||||
{"\x1e", `"\u001e"`},
|
||||
{"\x1f", `"\u001f"`},
|
||||
{"✭", `"✭"`},
|
||||
{"foo\xc2\x7fbar", `"foo\ufffd\u007fbar"`}, // invalid sequence
|
||||
{"ascii", `"ascii"`},
|
||||
{"\"a", `"\"a"`},
|
||||
{"\x1fa", `"\u001fa"`},
|
||||
{"foo\"bar\"baz", `"foo\"bar\"baz"`},
|
||||
{"\x1ffoo\x1fbar\x1fbaz", `"\u001ffoo\u001fbar\u001fbaz"`},
|
||||
{"emoji \u2764\ufe0f!", `"emoji ❤️!"`},
|
||||
}
|
||||
|
||||
var EncodeStringsTests = []struct {
|
||||
In []string
|
||||
Out string
|
||||
}{
|
||||
{nil, `[]`},
|
||||
{[]string{}, `[]`},
|
||||
{[]string{"A"}, `["A"]`},
|
||||
{[]string{"A", "B"}, `["A","B"]`},
|
||||
}
|
||||
|
||||
var EncodeStringerTests = []struct {
|
||||
In fmt.Stringer
|
||||
Out string
|
||||
}{
|
||||
{nil, `null`},
|
||||
{fmt.Stringer(nil), `null`},
|
||||
{net.IPv4bcast, `"255.255.255.255"`},
|
||||
}
|
||||
|
||||
var EncodeStringersTests = []struct {
|
||||
In []fmt.Stringer
|
||||
Out string
|
||||
}{
|
||||
{nil, `[]`},
|
||||
{[]fmt.Stringer{}, `[]`},
|
||||
{[]fmt.Stringer{net.IPv4bcast}, `["255.255.255.255"]`},
|
||||
{[]fmt.Stringer{net.IPv4allsys, net.IPv4allrouter}, `["224.0.0.1","224.0.0.2"]`},
|
||||
}
|
||||
|
||||
var TimeIntegerTestcases = []struct {
|
||||
Txt string
|
||||
Binary string
|
||||
RfcStr string
|
||||
UnixInt int
|
||||
}{
|
||||
{"2013-02-03T19:54:00-08:00", "\xc1\x1a\x51\x0f\x30\xd8", "2013-02-04T03:54:00Z", 1359950040},
|
||||
{"1950-02-03T19:54:00-08:00", "\xc1\x3a\x25\x71\x93\xa7", "1950-02-04T03:54:00Z", -628200360},
|
||||
}
|
||||
|
||||
var TimeFloatTestcases = []struct {
|
||||
RfcStr string
|
||||
Out string
|
||||
UnixInt int
|
||||
}{
|
||||
{"2006-01-02T15:04:05.999999-08:00", "\xc1\xfb\x41\xd0\xee\x6c\x59\x7f\xff\xfc", 1136243045},
|
||||
{"1956-01-02T15:04:05.999999-08:00", "\xc1\xfb\xc1\xba\x53\x81\x1a\x00\x00\x11", -441680155},
|
||||
}
|
||||
|
||||
var DurTestcases = []struct {
|
||||
Duration time.Duration
|
||||
FloatOut string
|
||||
IntegerOut string
|
||||
}{
|
||||
{1000, "\xfb\x3f\xf0\x00\x00\x00\x00\x00\x00", "\x01"},
|
||||
{2000, "\xfb\x40\x00\x00\x00\x00\x00\x00\x00", "\x02"},
|
||||
{200000, "\xfb\x40\x69\x00\x00\x00\x00\x00\x00", "\x18\xc8"},
|
||||
}
|
||||
|
||||
// inline copy from globals.go of InterfaceMarshalFunc used in tests to avoid import cycle
|
||||
func InterfaceMarshalFunc(v interface{}) ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
encoder := json.NewEncoder(&buf)
|
||||
encoder.SetEscapeHTML(false)
|
||||
err := encoder.Encode(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b := buf.Bytes()
|
||||
if len(b) > 0 {
|
||||
// Remove trailing \n which is added by Encode.
|
||||
return b[:len(b)-1], nil
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
@@ -507,6 +507,17 @@ func ExampleContext_IPAddr() {
|
||||
// Output: {"HostIP":"192.168.0.100","message":"hello world"}
|
||||
}
|
||||
|
||||
func ExampleContext_IPAddrs() {
|
||||
hostIP := net.IP{192, 168, 0, 100}
|
||||
log := zerolog.New(os.Stdout).With().
|
||||
IPAddrs("HostIP", []net.IP{hostIP}).
|
||||
Logger()
|
||||
|
||||
log.Log().Msg("hello world")
|
||||
|
||||
// Output: {"HostIP":["192.168.0.100"],"message":"hello world"}
|
||||
}
|
||||
|
||||
func ExampleContext_IPPrefix() {
|
||||
route := net.IPNet{IP: net.IP{192, 168, 0, 0}, Mask: net.CIDRMask(24, 32)}
|
||||
log := zerolog.New(os.Stdout).With().
|
||||
@@ -518,6 +529,17 @@ func ExampleContext_IPPrefix() {
|
||||
// Output: {"Route":"192.168.0.0/24","message":"hello world"}
|
||||
}
|
||||
|
||||
func ExampleContext_IPPrefixes() {
|
||||
route := net.IPNet{IP: net.IP{192, 168, 0, 0}, Mask: net.CIDRMask(24, 32)}
|
||||
log := zerolog.New(os.Stdout).With().
|
||||
IPPrefixes("Route", []net.IPNet{route}).
|
||||
Logger()
|
||||
|
||||
log.Log().Msg("hello world")
|
||||
|
||||
// Output: {"Route":["192.168.0.0/24"],"message":"hello world"}
|
||||
}
|
||||
|
||||
func ExampleContext_MACAddr() {
|
||||
mac := net.HardwareAddr{0x00, 0x14, 0x22, 0x01, 0x23, 0x45}
|
||||
log := zerolog.New(os.Stdout).With().
|
||||
@@ -560,3 +582,18 @@ func ExampleContext_Fields_slice() {
|
||||
|
||||
// Output: {"foo":"bar","bar":"baz","n":1,"message":"hello world"}
|
||||
}
|
||||
|
||||
func ExampleContext_Times() {
|
||||
t1 := time.Time{}
|
||||
t2 := t1.Add(time.Second * 10)
|
||||
t := []time.Time{t1, t2}
|
||||
|
||||
log := zerolog.New(os.Stdout).With().
|
||||
Str("foo", "bar").
|
||||
Times("times", t).
|
||||
Logger()
|
||||
|
||||
log.Log().Msg("hello world")
|
||||
|
||||
// Output: {"foo":"bar","times":["0001-01-01T00:00:00Z","0001-01-01T00:00:10Z"],"message":"hello world"}
|
||||
}
|
||||
|
||||
+272
-48
@@ -5,6 +5,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"net"
|
||||
"reflect"
|
||||
"runtime"
|
||||
@@ -121,17 +122,26 @@ func TestWith(t *testing.T) {
|
||||
Float32("float32", 11.101).
|
||||
Float64("float64", 12.30303).
|
||||
Time("time", time.Time{}).
|
||||
Ctx(context.Background())
|
||||
Dur("dur", 3).
|
||||
Ctx(context.Background()).
|
||||
Any("any", "test").
|
||||
Interface("interface", struct {
|
||||
Pub string
|
||||
Tag string `json:"tag"`
|
||||
}{"a", "b"}).
|
||||
Type("type", math.Phi)
|
||||
_, file, line, _ := runtime.Caller(0)
|
||||
caller := fmt.Sprintf("%s:%d", file, line+3)
|
||||
log := ctx.Caller().Logger()
|
||||
log.Log().Msg("")
|
||||
if got, want := decodeIfBinaryToString(out.Bytes()), `{"string":"foo","stringer":"127.0.0.1","stringer_nil":null,"bytes":"bar","hex":"12ef","json":{"some":"json"},"error":"some error","bool":true,"int":1,"int8":2,"int16":3,"int32":4,"int64":5,"uint":6,"uint8":7,"uint16":8,"uint32":9,"uint64":10,"float32":11.101,"float64":12.30303,"time":"0001-01-01T00:00:00Z","caller":"`+caller+`"}`+"\n"; got != want {
|
||||
if got, want := decodeIfBinaryToString(out.Bytes()),
|
||||
`{"string":"foo","stringer":"127.0.0.1","stringer_nil":null,"bytes":"bar","hex":"12ef","json":{"some":"json"},"error":"some error","bool":true,"int":1,"int8":2,"int16":3,"int32":4,"int64":5,"uint":6,"uint8":7,"uint16":8,"uint32":9,"uint64":10,"float32":11.101,"float64":12.30303,"time":"0001-01-01T00:00:00Z","dur":0.000003,"any":"test","interface":{"Pub":"a","tag":"b"},"type":"float64","caller":"`+caller+`"}`+"\n"; got != want {
|
||||
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
||||
}
|
||||
|
||||
// Validate CallerWithSkipFrameCount.
|
||||
out.Reset()
|
||||
ctx = New(out).With()
|
||||
_, file, line, _ = runtime.Caller(0)
|
||||
caller = fmt.Sprintf("%s:%d", file, line+5)
|
||||
log = ctx.CallerWithSkipFrameCount(3).Logger()
|
||||
@@ -140,7 +150,38 @@ func TestWith(t *testing.T) {
|
||||
}()
|
||||
// The above line is a little contrived, but the line above should be the line due
|
||||
// to the extra frame skip.
|
||||
if got, want := decodeIfBinaryToString(out.Bytes()), `{"string":"foo","stringer":"127.0.0.1","stringer_nil":null,"bytes":"bar","hex":"12ef","json":{"some":"json"},"error":"some error","bool":true,"int":1,"int8":2,"int16":3,"int32":4,"int64":5,"uint":6,"uint8":7,"uint16":8,"uint32":9,"uint64":10,"float32":11.101,"float64":12.30303,"time":"0001-01-01T00:00:00Z","caller":"`+caller+`"}`+"\n"; got != want {
|
||||
if got, want := decodeIfBinaryToString(out.Bytes()), `{"caller":"`+caller+`"}`+"\n"; got != want {
|
||||
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithPlurals(t *testing.T) {
|
||||
out := &bytes.Buffer{}
|
||||
ctx := New(out).With().
|
||||
Strs("strings", []string{"foo", "bar"}).
|
||||
Strs("strings_nil", nil).
|
||||
Stringers("stringers", []fmt.Stringer{net.IP{127, 0, 0, 1}, nil}).
|
||||
Stringers("stringers_nil", nil).
|
||||
Errs("errs", []error{errors.New("some error"), errors.New("some other error")}).
|
||||
Bools("bool", []bool{true, false}).
|
||||
Ints("int", []int{1, 2}).
|
||||
Ints8("int8", []int8{2, 3}).
|
||||
Ints16("int16", []int16{3, 4}).
|
||||
Ints32("int32", []int32{4, 5}).
|
||||
Ints64("int64", []int64{5, 6}).
|
||||
Uints("uint", []uint{6, 7}).
|
||||
Uints8("uint8", []uint8{7, 8}).
|
||||
Uints16("uint16", []uint16{8, 9}).
|
||||
Uints32("uint32", []uint32{9, 10}).
|
||||
Uints64("uint64", []uint64{10, 11}).
|
||||
Floats32("float32", []float32{1.1, 2.2}).
|
||||
Floats64("float64", []float64{2.2, 3.3}).
|
||||
Times("time", []time.Time{time.Time{}.AddDate(0, 1, 2), time.Time{}.AddDate(4, 5, 6)}).
|
||||
Durs("dur", []time.Duration{1 * time.Second, 2 * time.Second})
|
||||
log := ctx.Logger()
|
||||
log.Log().Msg("")
|
||||
if got, want := decodeIfBinaryToString(out.Bytes()),
|
||||
`{"strings":["foo","bar"],"strings_nil":[],"stringers":["127.0.0.1",null],"stringers_nil":null,"errs":["some error","some other error"],"bool":[true,false],"int":[1,2],"int8":[2,3],"int16":[3,4],"int32":[4,5],"int64":[5,6],"uint":[6,7],"uint8":[7,8],"uint16":[8,9],"uint32":[9,10],"uint64":[10,11],"float32":[1.1,2.2],"float64":[2.2,3.3],"time":["0001-02-03T00:00:00Z","0005-06-07T00:00:00Z"],"dur":[1000,2000]}`+"\n"; got != want {
|
||||
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
||||
}
|
||||
}
|
||||
@@ -156,6 +197,7 @@ func TestWithReset(t *testing.T) {
|
||||
Hex("hex", []byte{0x12, 0xef}).
|
||||
Uint64("uint64", 10).
|
||||
Float64("float64", 12.30303).
|
||||
Stack().
|
||||
Ctx(context.Background())
|
||||
log := ctx.Logger()
|
||||
log.Log().Msg("")
|
||||
@@ -188,9 +230,10 @@ func TestFieldsMap(t *testing.T) {
|
||||
"ipv6": net.IP{0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x34},
|
||||
"dur": 1 * time.Second,
|
||||
"time": time.Time{},
|
||||
"obj": obj{"a", "b", 1},
|
||||
"obj": fixtureObj{"a", "b", 1},
|
||||
"any": struct{ A string }{"test"},
|
||||
}).Msg("")
|
||||
if got, want := decodeIfBinaryToString(out.Bytes()), `{"bool":true,"bytes":"bar","dur":1000,"error":"some error","float32":11,"float64":12,"int":1,"int16":3,"int32":4,"int64":5,"int8":2,"ipv6":"2001:db8:85a3::8a2e:370:7334","nil":null,"obj":{"Pub":"a","Tag":"b","priv":1},"string":"foo","time":"0001-01-01T00:00:00Z","uint":6,"uint16":8,"uint32":9,"uint64":10,"uint8":7}`+"\n"; got != want {
|
||||
if got, want := decodeIfBinaryToString(out.Bytes()), `{"any":{"A":"test"},"bool":true,"bytes":"bar","dur":1000,"error":"some error","float32":11,"float64":12,"int":1,"int16":3,"int32":4,"int64":5,"int8":2,"ipv6":"2001:db8:85a3::8a2e:370:7334","nil":null,"obj":{"Pub":"a","Tag":"b","priv":1},"string":"foo","time":"0001-01-01T00:00:00Z","uint":6,"uint16":8,"uint32":9,"uint64":10,"uint8":7}`+"\n"; got != want {
|
||||
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
||||
}
|
||||
}
|
||||
@@ -291,7 +334,7 @@ func TestFieldsSlice(t *testing.T) {
|
||||
"ipv6", net.IP{0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x34},
|
||||
"dur", 1 * time.Second,
|
||||
"time", time.Time{},
|
||||
"obj", obj{"a", "b", 1},
|
||||
"obj", fixtureObj{"a", "b", 1},
|
||||
}).Msg("")
|
||||
if got, want := decodeIfBinaryToString(out.Bytes()), `{"nil":null,"string":"foo","bytes":"bar","error":"some error","bool":true,"int":1,"int8":2,"int16":3,"int32":4,"int64":5,"uint":6,"uint8":7,"uint16":8,"uint32":9,"uint64":10,"float32":11,"float64":12,"ipv6":"2001:db8:85a3::8a2e:370:7334","dur":1000,"time":"0001-01-01T00:00:00Z","obj":{"Pub":"a","Tag":"b","priv":1}}`+"\n"; got != want {
|
||||
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
||||
@@ -318,7 +361,7 @@ func TestFieldsNotMapSlice(t *testing.T) {
|
||||
out := &bytes.Buffer{}
|
||||
log := New(out)
|
||||
log.Log().
|
||||
Fields(obj{"a", "b", 1}).
|
||||
Fields(fixtureObj{"a", "b", 1}).
|
||||
Fields("string").
|
||||
Fields(1).
|
||||
Msg("")
|
||||
@@ -367,8 +410,12 @@ func TestFields(t *testing.T) {
|
||||
Time("time", time.Time{}).
|
||||
TimeDiff("diff", now, now.Add(-10*time.Second)).
|
||||
Ctx(context.Background()).
|
||||
Type("type", "hello").
|
||||
Any("any", struct{ A string }{"test"}).
|
||||
Any("logobject", fixtureObj{"a", "z", 1}).
|
||||
Stack().
|
||||
Msg("")
|
||||
if got, want := decodeIfBinaryToString(out.Bytes()), `{"caller":"`+caller+`","string":"foo","stringer":"127.0.0.1","stringer_nil":null,"bytes":"bar","hex":"12ef","json":{"some":"json"},"cbor":"data:application/cbor;base64,gwGCAgOCBAU=","func":"func_output","error":"some error","bool":true,"int":1,"int8":2,"int16":3,"int32":4,"int64":5,"uint":6,"uint8":7,"uint16":8,"uint32":9,"uint64":10,"ipv4":"192.168.0.100","ipv6":"2001:db8:85a3::8a2e:370:7334","mac":"00:14:22:01:23:45","pfxv4":"192.168.0.100/24","pfxv6":"2001:db8:85a3::8a2e:370:7334/64","float32":11.1234,"float64":12.321321321,"dur":1000,"time":"0001-01-01T00:00:00Z","diff":10000}`+"\n"; got != want {
|
||||
if got, want := decodeIfBinaryToString(out.Bytes()), `{"caller":"`+caller+`","string":"foo","stringer":"127.0.0.1","stringer_nil":null,"bytes":"bar","hex":"12ef","json":{"some":"json"},"cbor":"data:application/cbor;base64,gwGCAgOCBAU=","func":"func_output","error":"some error","bool":true,"int":1,"int8":2,"int16":3,"int32":4,"int64":5,"uint":6,"uint8":7,"uint16":8,"uint32":9,"uint64":10,"ipv4":"192.168.0.100","ipv6":"2001:db8:85a3::8a2e:370:7334","mac":"00:14:22:01:23:45","pfxv4":"192.168.0.100/24","pfxv6":"2001:db8:85a3::8a2e:370:7334/64","float32":11.1234,"float64":12.321321321,"dur":1000,"time":"0001-01-01T00:00:00Z","diff":10000,"type":"string","any":{"A":"test"},"logobject":{"Pub":"a","Tag":"z","priv":1}}`+"\n"; got != want {
|
||||
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
||||
}
|
||||
}
|
||||
@@ -495,6 +542,8 @@ func TestFieldsDisabled(t *testing.T) {
|
||||
IPPrefix("ip", net.IPNet{IP: net.IP{127, 0, 0, 1}, Mask: net.CIDRMask(24, 32)}).
|
||||
MACAddr("mac", net.HardwareAddr{0x00, 0x14, 0x22, 0x01, 0x23, 0x45}).
|
||||
Ctx(context.Background()).
|
||||
Any("any", struct{ A string }{"test"}).
|
||||
Interface("interface", fixtureObj{"a", "z", 1}).
|
||||
Msg("")
|
||||
if got, want := decodeIfBinaryToString(out.Bytes()), ""; got != want {
|
||||
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
||||
@@ -757,56 +806,231 @@ type loggableError struct {
|
||||
}
|
||||
|
||||
func (l loggableError) MarshalZerologObject(e *Event) {
|
||||
if l.error == nil {
|
||||
return
|
||||
}
|
||||
e.Str("message", l.error.Error()+": loggableError")
|
||||
}
|
||||
|
||||
func TestErrorMarshalFunc(t *testing.T) {
|
||||
out := &bytes.Buffer{}
|
||||
log := New(out)
|
||||
type nonLoggable struct {
|
||||
where string
|
||||
how int
|
||||
what string
|
||||
}
|
||||
|
||||
func TestErrorMarshalFunc_None(t *testing.T) {
|
||||
// test default behaviour
|
||||
log.Log().Err(errors.New("err")).Msg("msg")
|
||||
if got, want := decodeIfBinaryToString(out.Bytes()), `{"error":"err","message":"msg"}`+"\n"; got != want {
|
||||
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
||||
}
|
||||
out.Reset()
|
||||
|
||||
log.Log().Err(loggableError{errors.New("err")}).Msg("msg")
|
||||
if got, want := decodeIfBinaryToString(out.Bytes()), `{"error":{"message":"err: loggableError"},"message":"msg"}`+"\n"; got != want {
|
||||
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
||||
}
|
||||
out.Reset()
|
||||
|
||||
// test overriding the ErrorMarshalFunc
|
||||
originalErrorMarshalFunc := ErrorMarshalFunc
|
||||
defer func() {
|
||||
ErrorMarshalFunc = originalErrorMarshalFunc
|
||||
}()
|
||||
|
||||
ErrorMarshalFunc = func(err error) interface{} {
|
||||
testErrorMarshalFunc(t, nil, []string{
|
||||
"default",
|
||||
`{"message":"msg"}`,
|
||||
`{"error":"err","message":"msg"}`,
|
||||
`{"error":{"message":"err: loggableError"},"message":"msg"}`,
|
||||
`{"errs":[],"message":"msg"}`,
|
||||
`{"errs":["foo","bar"],"message":"msg"}`,
|
||||
})
|
||||
}
|
||||
func TestErrorMarshalFunc_AppendedString(t *testing.T) {
|
||||
// test returning an appended string from ErrorMarshalFunc
|
||||
testErrorMarshalFunc(t, func(err error) interface{} {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return err.Error() + ": marshaled string"
|
||||
}
|
||||
log.Log().Err(errors.New("err")).Msg("msg")
|
||||
if got, want := decodeIfBinaryToString(out.Bytes()), `{"error":"err: marshaled string","message":"msg"}`+"\n"; got != want {
|
||||
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
||||
}
|
||||
}, []string{
|
||||
"appended string",
|
||||
`{"message":"msg"}`,
|
||||
`{"error":"err: marshaled string","message":"msg"}`,
|
||||
`{"error":"err: marshaled string","message":"msg"}`,
|
||||
`{"errs":[],"message":"msg"}`,
|
||||
`{"errs":["foo: marshaled string","bar: marshaled string"],"message":"msg"}`,
|
||||
})
|
||||
}
|
||||
func TestErrorMarshalFunc_NilError(t *testing.T) {
|
||||
// test returning an nil error from ErrorMarshalFunc
|
||||
testErrorMarshalFunc(t, func(err error) interface{} {
|
||||
return error(nil)
|
||||
}, []string{
|
||||
"nil error",
|
||||
`{"message":"msg"}`,
|
||||
`{"message":"msg"}`,
|
||||
`{"message":"msg"}`,
|
||||
`{"errs":[],"message":"msg"}`,
|
||||
`{"errs":[null,null],"message":"msg"}`,
|
||||
})
|
||||
}
|
||||
|
||||
out.Reset()
|
||||
ErrorMarshalFunc = func(err error) interface{} {
|
||||
return errors.New(err.Error() + ": new error")
|
||||
}
|
||||
log.Log().Err(errors.New("err")).Msg("msg")
|
||||
if got, want := decodeIfBinaryToString(out.Bytes()), `{"error":"err: new error","message":"msg"}`+"\n"; got != want {
|
||||
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
||||
}
|
||||
func TestErrorMarshalFunc_UntypedNil(t *testing.T) {
|
||||
// test returning an untyped nil from ErrorMarshalFunc
|
||||
testErrorMarshalFunc(t, func(err error) interface{} {
|
||||
return nil
|
||||
}, []string{
|
||||
"untyped nil",
|
||||
`{"message":"msg"}`,
|
||||
`{"message":"msg"}`,
|
||||
`{"message":"msg"}`,
|
||||
`{"errs":[],"message":"msg"}`,
|
||||
`{"errs":[null,null],"message":"msg"}`,
|
||||
})
|
||||
}
|
||||
|
||||
out.Reset()
|
||||
ErrorMarshalFunc = func(err error) interface{} {
|
||||
type wrappedError struct {
|
||||
error
|
||||
msg string
|
||||
}
|
||||
|
||||
func (w wrappedError) Error() string {
|
||||
if w.error == nil {
|
||||
return w.msg
|
||||
}
|
||||
return w.error.Error() + ": " + w.msg
|
||||
}
|
||||
|
||||
func TestErrorMarshalFunc_WrappedError(t *testing.T) {
|
||||
// test returning an wrapped error from ErrorMarshalFunc
|
||||
testErrorMarshalFunc(t, func(err error) interface{} {
|
||||
if err == nil {
|
||||
return nil
|
||||
} else if we, ok := err.(wrappedError); ok {
|
||||
return we
|
||||
} else {
|
||||
return wrappedError{err, "addendum"}
|
||||
}
|
||||
}, []string{
|
||||
"wrapped error",
|
||||
`{"message":"msg"}`,
|
||||
`{"error":"err: addendum","message":"msg"}`,
|
||||
`{"error":"err: addendum","message":"msg"}`,
|
||||
`{"errs":[],"message":"msg"}`,
|
||||
`{"errs":["foo: addendum","bar: addendum"],"message":"msg"}`,
|
||||
})
|
||||
}
|
||||
|
||||
func TestErrorMarshalFunc_LoggableType(t *testing.T) {
|
||||
// test returning a loggable type from ErrorMarshalFunc
|
||||
testErrorMarshalFunc(t, func(err error) interface{} {
|
||||
return loggableError{err}
|
||||
}, []string{
|
||||
"loggable type",
|
||||
`{"error":{},"message":"msg"}`,
|
||||
`{"error":{"message":"err: loggableError"},"message":"msg"}`,
|
||||
`{"error":{"message":"err: loggableError"},"message":"msg"}`,
|
||||
`{"errs":[],"message":"msg"}`,
|
||||
`{"errs":[{"message":"foo: loggableError"},{"message":"bar: loggableError"}],"message":"msg"}`,
|
||||
})
|
||||
}
|
||||
func TestErrorMarshalFunc_String(t *testing.T) {
|
||||
// test returning a string type from ErrorMarshalFunc
|
||||
testErrorMarshalFunc(t, func(err error) interface{} {
|
||||
return "i'm an err" // return just a string
|
||||
}, []string{
|
||||
"string type",
|
||||
`{"error":"i'm an err","message":"msg"}`,
|
||||
`{"error":"i'm an err","message":"msg"}`,
|
||||
`{"error":"i'm an err","message":"msg"}`,
|
||||
`{"errs":[],"message":"msg"}`,
|
||||
`{"errs":["i'm an err","i'm an err"],"message":"msg"}`,
|
||||
})
|
||||
}
|
||||
|
||||
func TestErrorMarshalFunc_NonLoggableType(t *testing.T) {
|
||||
// test returning a non-loggable type from ErrorMarshalFunc
|
||||
testErrorMarshalFunc(t, func(err error) interface{} {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return nonLoggable{where: "here", how: 42, what: err.Error()}
|
||||
}, []string{
|
||||
"non-loggable type",
|
||||
`{"message":"msg"}`,
|
||||
`{"error":{},"message":"msg"}`,
|
||||
`{"error":{},"message":"msg"}`,
|
||||
`{"errs":[],"message":"msg"}`,
|
||||
`{"errs":[{},{}],"message":"msg"}`,
|
||||
})
|
||||
}
|
||||
|
||||
func testErrorMarshalFunc(t *testing.T, errFunc func(err error) interface{}, wants []string) {
|
||||
if errFunc != nil {
|
||||
originalErrorMarshalFunc := ErrorMarshalFunc
|
||||
defer func() {
|
||||
ErrorMarshalFunc = originalErrorMarshalFunc
|
||||
}()
|
||||
|
||||
ErrorMarshalFunc = errFunc
|
||||
}
|
||||
log.Log().Err(errors.New("err")).Msg("msg")
|
||||
if got, want := decodeIfBinaryToString(out.Bytes()), `{"error":{"message":"err: loggableError"},"message":"msg"}`+"\n"; got != want {
|
||||
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
||||
|
||||
testcase := wants[0]
|
||||
var err error
|
||||
|
||||
err = nil
|
||||
testContextErrorMarshalFunc(t, testcase+" / ctx.Err(nil)", wants[1], func(ctx Context) Context {
|
||||
ctx = ctx.Err(err)
|
||||
return ctx
|
||||
})
|
||||
testEventErrorMarshalFunc(t, testcase+" / e.Err(nil)", wants[1], func(e *Event) *Event {
|
||||
e.Err(err)
|
||||
return e
|
||||
})
|
||||
|
||||
err = errors.New("err")
|
||||
testContextErrorMarshalFunc(t, testcase+" / ctx.Err(error)", wants[2], func(ctx Context) Context {
|
||||
ctx = ctx.Err(err)
|
||||
return ctx
|
||||
})
|
||||
testEventErrorMarshalFunc(t, testcase+" / e.Err(error)", wants[2], func(e *Event) *Event {
|
||||
e.Err(err)
|
||||
return e
|
||||
})
|
||||
|
||||
err = loggableError{errors.New("err")}
|
||||
testContextErrorMarshalFunc(t, testcase+" / ctx.Err(loggable)", wants[3], func(ctx Context) Context {
|
||||
ctx = ctx.Err(err)
|
||||
return ctx
|
||||
})
|
||||
testEventErrorMarshalFunc(t, testcase+" / e.Err(loggable)", wants[3], func(e *Event) *Event {
|
||||
e.Err(err)
|
||||
return e
|
||||
})
|
||||
|
||||
var errs []error = nil
|
||||
testContextErrorMarshalFunc(t, testcase+" / ctx.Errs(nil)", wants[4], func(ctx Context) Context {
|
||||
ctx = ctx.Errs("errs", errs)
|
||||
return ctx
|
||||
})
|
||||
testEventErrorMarshalFunc(t, testcase+" / e.Errs(nil)", wants[4], func(e *Event) *Event {
|
||||
e.Errs("errs", errs)
|
||||
return e
|
||||
})
|
||||
|
||||
errs = []error{errors.New("foo"), errors.New("bar")}
|
||||
testContextErrorMarshalFunc(t, testcase+" / ctx.Errs([])", wants[5], func(ctx Context) Context {
|
||||
ctx = ctx.Errs("errs", []error{errors.New("foo"), errors.New("bar")})
|
||||
return ctx
|
||||
})
|
||||
testEventErrorMarshalFunc(t, testcase+" / e.Errs([])", wants[5], func(e *Event) *Event {
|
||||
e.Errs("errs", errs)
|
||||
return e
|
||||
})
|
||||
}
|
||||
|
||||
func testEventErrorMarshalFunc(t *testing.T, testcase string, wants string, test func(e *Event) *Event) {
|
||||
out := &bytes.Buffer{}
|
||||
logger := New(out)
|
||||
test(logger.Log()).Msg("msg")
|
||||
if got, want := decodeIfBinaryToString(out.Bytes()), wants+"\n"; got != want {
|
||||
t.Errorf("%s output:\ngot: %v\nwant: %v", testcase, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func testContextErrorMarshalFunc(t *testing.T, testcase string, wants string, test func(ctx Context) Context) {
|
||||
out := &bytes.Buffer{}
|
||||
ctx := New(out).With()
|
||||
|
||||
ctx = test(ctx)
|
||||
logger := ctx.Logger()
|
||||
logger.Log().Msg("msg")
|
||||
if got, want := decodeIfBinaryToString(out.Bytes()), wants+"\n"; got != want {
|
||||
t.Errorf("%s output:\ngot: %v\nwant: %v", testcase, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user