1
0
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:
Marc Brooks
2025-12-15 13:55:37 -06:00
committed by GitHub
parent 5391dd7c34
commit 8148645974
26 changed files with 3784 additions and 968 deletions
+2
View File
@@ -23,3 +23,5 @@ _testmain.go
*.exe
*.test
*.prof
coverage.out
+2 -10
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)
}
}
+13 -16
View File
@@ -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)
+159
View File
@@ -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),
}
}
+22
View File
@@ -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))
}
}
+60 -57
View File
@@ -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)
}
+58
View File
@@ -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
View File
@@ -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{
File diff suppressed because it is too large Load Diff
+30
View File
@@ -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))
}
}
+2 -2
View File
@@ -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
+10 -8
View File
@@ -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)
}
}
}
+433
View File
@@ -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)
}
}
}
+356
View File
@@ -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
}
+2 -2
View File
@@ -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
+42 -63
View File
@@ -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)
}
}
}
+234
View File
@@ -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
View File
@@ -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)
}
}
}
+373
View File
@@ -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
}
+37
View File
@@ -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
View File
@@ -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)
}
}