1
0
mirror of https://github.com/rs/zerolog synced 2026-06-08 17:13:30 +00:00

feat: add new global DurationFieldFormat (#733)

This deprecates DurationFieldInteger in favor of DurationFieldFormat with the value of DurationFormatInt
This commit is contained in:
Sebastian Rühl
2026-01-05 19:16:40 +01:00
committed by GitHub
parent 05348ba6f6
commit 4685edb80b
13 changed files with 1010 additions and 688 deletions
+1 -1
View File
@@ -193,7 +193,7 @@ func (a *Array) Time(t time.Time) *Array {
// Dur appends d to the array.
func (a *Array) Dur(d time.Duration) *Array {
a.buf = enc.AppendDuration(enc.AppendArrayDelim(a.buf), d, DurationFieldUnit, DurationFieldInteger, FloatingPointPrecision)
a.buf = enc.AppendDuration(enc.AppendArrayDelim(a.buf), d, DurationFieldUnit, DurationFieldFormat, DurationFieldInteger, FloatingPointPrecision)
return a
}
+2 -2
View File
@@ -400,13 +400,13 @@ func (c Context) Times(key string, t []time.Time) Context {
// Dur adds the field key with d divided by unit and stored as a float.
func (c Context) Dur(key string, d time.Duration) Context {
c.l.context = enc.AppendDuration(enc.AppendKey(c.l.context, key), d, DurationFieldUnit, DurationFieldInteger, FloatingPointPrecision)
c.l.context = enc.AppendDuration(enc.AppendKey(c.l.context, key), d, DurationFieldUnit, DurationFieldFormat, DurationFieldInteger, FloatingPointPrecision)
return c
}
// Durs adds the field key with d divided by unit and stored as a float.
func (c Context) Durs(key string, d []time.Duration) Context {
c.l.context = enc.AppendDurations(enc.AppendKey(c.l.context, key), d, DurationFieldUnit, DurationFieldInteger, FloatingPointPrecision)
c.l.context = enc.AppendDurations(enc.AppendKey(c.l.context, key), d, DurationFieldUnit, DurationFieldFormat, DurationFieldInteger, FloatingPointPrecision)
return c
}
+2 -2
View File
@@ -13,8 +13,8 @@ type encoder interface {
AppendBool(dst []byte, val bool) []byte
AppendBools(dst []byte, vals []bool) []byte
AppendBytes(dst, s []byte) []byte
AppendDuration(dst []byte, d time.Duration, unit time.Duration, useInt bool, precision int) []byte
AppendDurations(dst []byte, vals []time.Duration, unit time.Duration, useInt bool, precision int) []byte
AppendDuration(dst []byte, d time.Duration, unit time.Duration, format string, useInt bool, precision int) []byte
AppendDurations(dst []byte, vals []time.Duration, unit time.Duration, format string, useInt bool, precision int) []byte
AppendEndMarker(dst []byte) []byte
AppendFloat32(dst []byte, val float32, precision int) []byte
AppendFloat64(dst []byte, val float64, precision int) []byte
+3 -3
View File
@@ -729,7 +729,7 @@ func (e *Event) Dur(key string, d time.Duration) *Event {
if e == nil {
return e
}
e.buf = enc.AppendDuration(enc.AppendKey(e.buf, key), d, DurationFieldUnit, DurationFieldInteger, FloatingPointPrecision)
e.buf = enc.AppendDuration(enc.AppendKey(e.buf, key), d, DurationFieldUnit, DurationFieldFormat, DurationFieldInteger, FloatingPointPrecision)
return e
}
@@ -740,7 +740,7 @@ func (e *Event) Durs(key string, d []time.Duration) *Event {
if e == nil {
return e
}
e.buf = enc.AppendDurations(enc.AppendKey(e.buf, key), d, DurationFieldUnit, DurationFieldInteger, FloatingPointPrecision)
e.buf = enc.AppendDurations(enc.AppendKey(e.buf, key), d, DurationFieldUnit, DurationFieldFormat, DurationFieldInteger, FloatingPointPrecision)
return e
}
@@ -755,7 +755,7 @@ func (e *Event) TimeDiff(key string, t time.Time, start time.Time) *Event {
if t.After(start) {
d = t.Sub(start)
}
e.buf = enc.AppendDuration(enc.AppendKey(e.buf, key), d, DurationFieldUnit, DurationFieldInteger, FloatingPointPrecision)
e.buf = enc.AppendDuration(enc.AppendKey(e.buf, key), d, DurationFieldUnit, DurationFieldFormat, DurationFieldInteger, FloatingPointPrecision)
return e
}
+3 -3
View File
@@ -151,7 +151,7 @@ func appendFieldList(dst []byte, kvList []interface{}, stack bool) []byte {
case time.Time:
dst = enc.AppendTime(dst, val, TimeFieldFormat)
case time.Duration:
dst = enc.AppendDuration(dst, val, DurationFieldUnit, DurationFieldInteger, FloatingPointPrecision)
dst = enc.AppendDuration(dst, val, DurationFieldUnit, DurationFieldFormat, DurationFieldInteger, FloatingPointPrecision)
case *string:
if val != nil {
dst = enc.AppendString(dst, *val)
@@ -244,7 +244,7 @@ func appendFieldList(dst []byte, kvList []interface{}, stack bool) []byte {
}
case *time.Duration:
if val != nil {
dst = enc.AppendDuration(dst, *val, DurationFieldUnit, DurationFieldInteger, FloatingPointPrecision)
dst = enc.AppendDuration(dst, *val, DurationFieldUnit, DurationFieldFormat, DurationFieldInteger, FloatingPointPrecision)
} else {
dst = enc.AppendNil(dst)
}
@@ -278,7 +278,7 @@ func appendFieldList(dst []byte, kvList []interface{}, stack bool) []byte {
case []time.Time:
dst = enc.AppendTimes(dst, val, TimeFieldFormat)
case []time.Duration:
dst = enc.AppendDurations(dst, val, DurationFieldUnit, DurationFieldInteger, FloatingPointPrecision)
dst = enc.AppendDurations(dst, val, DurationFieldUnit, DurationFieldFormat, DurationFieldInteger, FloatingPointPrecision)
case nil:
dst = enc.AppendNil(dst)
case net.IP:
+14
View File
@@ -24,6 +24,16 @@ const (
// TimeFormatUnixNano defines a time format that makes time fields to be
// serialized as Unix timestamp integers in nanoseconds.
TimeFormatUnixNano = "UNIXNANO"
// DurationFormatFloat defines a format for Duration fields that makes duration fields to be
// serialized as floating point numbers.
DurationFormatFloat = "float"
// DurationFormatInt defines a format for Duration fields that makes duration fields to be
// serialized as integers.
DurationFormatInt = "int"
// DurationFormatString defines a format for Duration fields that makes duration fields to be
// serialized as string.
DurationFormatString = "string"
)
var (
@@ -107,12 +117,16 @@ var (
// TimestampFunc defines the function called to generate a timestamp.
TimestampFunc = time.Now
// DurationFieldFormat defines the format of the Duration field type.
DurationFieldFormat = DurationFormatFloat
// DurationFieldUnit defines the unit for time.Duration type fields added
// using the Dur method.
DurationFieldUnit = time.Millisecond
// DurationFieldInteger renders Dur fields as integer instead of float if
// set to true.
// Deprecated: use DurationFieldFormat with DurationFormatInt instead.
DurationFieldInteger = false
// ErrorHandler is called whenever zerolog fails to write an event on its
+22 -22
View File
@@ -1,22 +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))
}
}
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))
}
}
+22 -3
View File
@@ -4,6 +4,17 @@ import (
"time"
)
const (
// Import from zerolog/global.go
timeFormatUnix = ""
timeFormatUnixMs = "UNIXMS"
timeFormatUnixMicro = "UNIXMICRO"
timeFormatUnixNano = "UNIXNANO"
durationFormatFloat = "float"
durationFormatInt = "int"
durationFormatString = "string"
)
func appendIntegerTimestamp(dst []byte, t time.Time) []byte {
major := majorTypeTags
minor := additionalTypeTimestamp
@@ -63,17 +74,25 @@ func (e Encoder) AppendTimes(dst []byte, vals []time.Time, unused string) []byte
// AppendDuration encodes and adds a duration to the dst byte array.
// useInt field indicates whether to store the duration as seconds (integer) or
// as seconds+nanoseconds (float).
func (e Encoder) AppendDuration(dst []byte, d time.Duration, unit time.Duration, useInt bool, unused int) []byte {
func (e Encoder) AppendDuration(dst []byte, d time.Duration, unit time.Duration, format string, useInt bool, unused int) []byte {
if useInt {
return e.AppendInt64(dst, int64(d/unit))
}
switch format {
case durationFormatFloat:
return e.AppendFloat64(dst, float64(d)/float64(unit), unused)
case durationFormatInt:
return e.AppendInt64(dst, int64(d/unit))
case durationFormatString:
return e.AppendString(dst, d.String())
}
return e.AppendFloat64(dst, float64(d)/float64(unit), unused)
}
// AppendDurations encodes and adds an array of durations to the dst byte array.
// useInt field indicates whether to store the duration as seconds (integer) or
// as seconds+nanoseconds (float).
func (e Encoder) AppendDurations(dst []byte, vals []time.Duration, unit time.Duration, useInt bool, unused int) []byte {
func (e Encoder) AppendDurations(dst []byte, vals []time.Duration, unit time.Duration, format string, useInt bool, unused int) []byte {
major := majorTypeArray
l := len(vals)
if l == 0 {
@@ -86,7 +105,7 @@ func (e Encoder) AppendDurations(dst []byte, vals []time.Duration, unit time.Dur
dst = appendCborTypePrefix(dst, major, uint64(l))
}
for _, d := range vals {
dst = e.AppendDuration(dst, d, unit, useInt, unused)
dst = e.AppendDuration(dst, d, unit, format, useInt, unused)
}
return dst
}
+144 -5
View File
@@ -5,12 +5,151 @@ import (
"encoding/hex"
"fmt"
"math"
"reflect"
"testing"
"time"
"github.com/rs/zerolog/internal"
)
func TestEncoder_AppendDuration(t *testing.T) {
type args struct {
dst []byte
d time.Duration
unit time.Duration
format string
useInt bool
unused int
}
tests := []struct {
name string
args args
want []byte
}{
{
name: "useInt",
args: args{
d: 1234567890,
unit: time.Second,
useInt: true,
},
want: []byte{1},
},
{
name: "formatFloat",
args: args{
d: 1234567890,
unit: time.Second,
format: durationFormatFloat,
},
want: []byte{251, 63, 243, 192, 202, 66, 131, 222, 27},
},
{
name: "formatInt",
args: args{
d: 1234567890,
unit: time.Second,
format: durationFormatInt,
},
want: []byte{1},
},
{
name: "formatString",
args: args{
d: 1234567890,
unit: time.Second,
format: durationFormatString,
},
want: []byte{107, 49, 46, 50, 51, 52, 53, 54, 55, 56, 57, 115},
},
{
name: "formatBlank",
args: args{
d: 1234567890,
unit: time.Second,
},
want: []byte{251, 63, 243, 192, 202, 66, 131, 222, 27},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
e := Encoder{}
if got := e.AppendDuration(tt.args.dst, tt.args.d, tt.args.unit, tt.args.format, tt.args.useInt, tt.args.unused); !reflect.DeepEqual(got, tt.want) {
t.Errorf("AppendDuration() = %v, want %v", got, tt.want)
}
})
}
}
func TestEncoder_AppendDurations(t *testing.T) {
type args struct {
dst []byte
vals []time.Duration
unit time.Duration
format string
useInt bool
unused int
}
tests := []struct {
name string
args args
want []byte
}{
{
name: "useInt",
args: args{
vals: []time.Duration{1234567890},
unit: time.Second,
useInt: true,
},
want: []byte{129, 1},
},
{
name: "formatFloat",
args: args{
vals: []time.Duration{1234567890},
unit: time.Second,
format: durationFormatFloat,
},
want: []byte{129, 251, 63, 243, 192, 202, 66, 131, 222, 27},
},
{
name: "formatInt",
args: args{
vals: []time.Duration{1234567890},
unit: time.Second,
format: durationFormatInt,
},
want: []byte{129, 1},
},
{
name: "formatString",
args: args{
vals: []time.Duration{1234567890},
unit: time.Second,
format: durationFormatString,
},
want: []byte{129, 107, 49, 46, 50, 51, 52, 53, 54, 55, 56, 57, 115},
},
{
name: "formatBlank",
args: args{
vals: []time.Duration{1234567890},
unit: time.Second,
},
want: []byte{129, 251, 63, 243, 192, 202, 66, 131, 222, 27},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
e := Encoder{}
if got := e.AppendDurations(tt.args.dst, tt.args.vals, tt.args.unit, tt.args.format, tt.args.useInt, tt.args.unused); !reflect.DeepEqual(got, tt.want) {
t.Errorf("AppendDurations() = %v, want %v", got, tt.want)
}
})
}
}
func TestAppendTimeNow(t *testing.T) {
tm := time.Now()
s := enc.AppendTime([]byte{}, tm, "unused")
@@ -116,7 +255,7 @@ func TestAppendDurationFloat(t *testing.T) {
dur := tt.Duration
want := []byte{}
want = append(want, []byte(tt.FloatOut)...)
got := enc.AppendDuration([]byte{}, dur, time.Microsecond, false, -1)
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,
@@ -130,7 +269,7 @@ func TestAppendDurationInteger(t *testing.T) {
dur := tt.Duration
want := []byte{}
want = append(want, []byte(tt.IntegerOut)...)
got := enc.AppendDuration([]byte{}, dur, time.Microsecond, true, -1)
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,
@@ -148,7 +287,7 @@ func TestAppendDurations(t *testing.T) {
want = append(want, []byte(tt.FloatOut)...)
}
got := enc.AppendDurations([]byte{}, array, time.Microsecond, false, -1)
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),
@@ -160,7 +299,7 @@ func TestAppendDurations(t *testing.T) {
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)
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),
@@ -178,7 +317,7 @@ func TestAppendDurations(t *testing.T) {
array[i] = testtime
want = append(want, []byte(outbytes)...)
}
got = enc.AppendDurations([]byte{}, array, time.Microsecond, false, -1)
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,
+30 -30
View File
@@ -1,30 +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))
}
}
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))
}
}
+19 -8
View File
@@ -7,10 +7,13 @@ import (
const (
// Import from zerolog/global.go
timeFormatUnix = ""
timeFormatUnixMs = "UNIXMS"
timeFormatUnixMicro = "UNIXMICRO"
timeFormatUnixNano = "UNIXNANO"
timeFormatUnix = ""
timeFormatUnixMs = "UNIXMS"
timeFormatUnixMicro = "UNIXMICRO"
timeFormatUnixNano = "UNIXNANO"
durationFormatFloat = "float"
durationFormatInt = "int"
durationFormatString = "string"
)
// AppendTime formats the input time with the given format
@@ -88,24 +91,32 @@ func appendUnixNanoTimes(dst []byte, vals []time.Time, div int64) []byte {
// AppendDuration formats the input duration with the given unit & format
// and appends the encoded string to the input byte slice.
func (e Encoder) AppendDuration(dst []byte, d time.Duration, unit time.Duration, useInt bool, precision int) []byte {
func (e Encoder) AppendDuration(dst []byte, d time.Duration, unit time.Duration, format string, useInt bool, precision int) []byte {
if useInt {
return strconv.AppendInt(dst, int64(d/unit), 10)
}
switch format {
case durationFormatFloat:
return e.AppendFloat64(dst, float64(d)/float64(unit), precision)
case durationFormatInt:
return e.AppendInt64(dst, int64(d/unit))
case durationFormatString:
return e.AppendString(dst, d.String())
}
return e.AppendFloat64(dst, float64(d)/float64(unit), precision)
}
// AppendDurations formats the input durations with the given unit & format
// and appends the encoded string list to the input byte slice.
func (e Encoder) AppendDurations(dst []byte, vals []time.Duration, unit time.Duration, useInt bool, precision int) []byte {
func (e Encoder) AppendDurations(dst []byte, vals []time.Duration, unit time.Duration, format string, useInt bool, precision int) []byte {
if len(vals) == 0 {
return append(dst, '[', ']')
}
dst = append(dst, '[')
dst = e.AppendDuration(dst, vals[0], unit, useInt, precision)
dst = e.AppendDuration(dst, vals[0], unit, format, useInt, precision)
if len(vals) > 1 {
for _, d := range vals[1:] {
dst = e.AppendDuration(append(dst, ','), d, unit, useInt, precision)
dst = e.AppendDuration(append(dst, ','), d, unit, format, useInt, precision)
}
}
dst = append(dst, ']')
+373 -234
View File
@@ -1,234 +1,373 @@
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")
}
})
}
}
package json
import (
"bytes"
"fmt"
"reflect"
"testing"
"time"
"github.com/rs/zerolog/internal"
)
func TestEncoder_AppendDuration(t *testing.T) {
type args struct {
dst []byte
d time.Duration
unit time.Duration
format string
useInt bool
unused int
}
tests := []struct {
name string
args args
want []byte
}{
{
name: "useInt",
args: args{
d: 1234567890,
unit: time.Second,
useInt: true,
},
want: []byte{49},
},
{
name: "formatFloat",
args: args{
d: 1234567890,
unit: time.Second,
format: durationFormatFloat,
},
want: []byte{49},
},
{
name: "formatInt",
args: args{
d: 1234567890,
unit: time.Second,
format: durationFormatInt,
},
want: []byte{49},
},
{
name: "formatString",
args: args{
d: 1234567890,
unit: time.Second,
format: durationFormatString,
},
want: []byte{34, 49, 46, 50, 51, 52, 53, 54, 55, 56, 57, 115, 34},
},
{
name: "formatBlank",
args: args{
d: 1234567890,
unit: time.Second,
},
want: []byte{49},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
e := Encoder{}
if got := e.AppendDuration(tt.args.dst, tt.args.d, tt.args.unit, tt.args.format, tt.args.useInt, tt.args.unused); !reflect.DeepEqual(got, tt.want) {
t.Errorf("AppendDuration() = %v, want %v", got, tt.want)
}
})
}
}
func TestEncoder_AppendDurations(t *testing.T) {
type args struct {
dst []byte
vals []time.Duration
unit time.Duration
format string
useInt bool
unused int
}
tests := []struct {
name string
args args
want []byte
}{
{
name: "useInt",
args: args{
vals: []time.Duration{1234567890},
unit: time.Second,
useInt: true,
},
want: []byte{91, 49, 93},
},
{
name: "formatFloat",
args: args{
vals: []time.Duration{1234567890},
unit: time.Second,
format: durationFormatFloat,
},
want: []byte{91, 49, 93},
},
{
name: "formatInt",
args: args{
vals: []time.Duration{1234567890},
unit: time.Second,
format: durationFormatInt,
},
want: []byte{91, 49, 93},
},
{
name: "formatString",
args: args{
vals: []time.Duration{1234567890},
unit: time.Second,
format: durationFormatString,
},
want: []byte{91, 34, 49, 46, 50, 51, 52, 53, 54, 55, 56, 57, 115, 34, 93},
},
{
name: "formatBlank",
args: args{
vals: []time.Duration{1234567890},
unit: time.Second,
},
want: []byte{91, 49, 93},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
e := Encoder{}
if got := e.AppendDurations(tt.args.dst, tt.args.vals, tt.args.unit, tt.args.format, tt.args.useInt, tt.args.unused); !reflect.DeepEqual(got, tt.want) {
t.Errorf("AppendDurations() = %v, want %v", got, tt.want)
}
})
}
}
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")
}
})
}
}
+375 -375
View File
@@ -1,375 +1,375 @@
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
Binary string
}{
{nil, `null`, "\xf6"},
{fmt.Stringer(nil), `null`, "\xf6"},
{net.IPv4bcast, `"255.255.255.255"`, "\x6f\x32\x35\x35\x2e\x32\x35\x35\x2e\x32\x35\x35\x2e\x32\x35\x35"},
}
var EncodeStringersTests = []struct {
In []fmt.Stringer
Out string
Binary string
}{
{nil, `[]`, "\x9f\xff"},
{[]fmt.Stringer{}, `[]`, "\x9f\xff"},
{[]fmt.Stringer{net.IPv4bcast}, `["255.255.255.255"]`, "\x9f\x6f255.255.255.255\xff"},
{[]fmt.Stringer{net.IPv4allsys, net.IPv4allrouter}, `["224.0.0.1","224.0.0.2"]`, "\x9f\x69224.0.0.1\x69224.0.0.2\xff"},
}
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
}
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
Binary string
}{
{nil, `null`, "\xf6"},
{fmt.Stringer(nil), `null`, "\xf6"},
{net.IPv4bcast, `"255.255.255.255"`, "\x6f\x32\x35\x35\x2e\x32\x35\x35\x2e\x32\x35\x35\x2e\x32\x35\x35"},
}
var EncodeStringersTests = []struct {
In []fmt.Stringer
Out string
Binary string
}{
{nil, `[]`, "\x9f\xff"},
{[]fmt.Stringer{}, `[]`, "\x9f\xff"},
{[]fmt.Stringer{net.IPv4bcast}, `["255.255.255.255"]`, "\x9f\x6f255.255.255.255\xff"},
{[]fmt.Stringer{net.IPv4allsys, net.IPv4allrouter}, `["224.0.0.1","224.0.0.2"]`, "\x9f\x69224.0.0.1\x69224.0.0.2\xff"},
}
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
}