diff --git a/array.go b/array.go index 1f45828..2ce789f 100644 --- a/array.go +++ b/array.go @@ -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 } diff --git a/context.go b/context.go index 9e24c16..b8a3cea 100644 --- a/context.go +++ b/context.go @@ -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 } diff --git a/encoder.go b/encoder.go index 4dbaf38..652380c 100644 --- a/encoder.go +++ b/encoder.go @@ -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 diff --git a/event.go b/event.go index 2c95c13..c84e935 100644 --- a/event.go +++ b/event.go @@ -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 } diff --git a/fields.go b/fields.go index 5f7ef5a..3d35ef3 100644 --- a/fields.go +++ b/fields.go @@ -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: diff --git a/globals.go b/globals.go index 9a9be71..e34d0fc 100644 --- a/globals.go +++ b/globals.go @@ -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 diff --git a/internal/cbor/base_test.go b/internal/cbor/base_test.go index 88e8dc1..5132f9a 100644 --- a/internal/cbor/base_test.go +++ b/internal/cbor/base_test.go @@ -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)) + } +} diff --git a/internal/cbor/time.go b/internal/cbor/time.go index a92c3ac..818f930 100644 --- a/internal/cbor/time.go +++ b/internal/cbor/time.go @@ -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 } diff --git a/internal/cbor/time_test.go b/internal/cbor/time_test.go index 3c46ec7..5e6f815 100644 --- a/internal/cbor/time_test.go +++ b/internal/cbor/time_test.go @@ -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, diff --git a/internal/json/base_test.go b/internal/json/base_test.go index 0a2e37a..13c2ea8 100644 --- a/internal/json/base_test.go +++ b/internal/json/base_test.go @@ -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)) + } +} diff --git a/internal/json/time.go b/internal/json/time.go index 08cbbd9..2783d10 100644 --- a/internal/json/time.go +++ b/internal/json/time.go @@ -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, ']') diff --git a/internal/json/time_test.go b/internal/json/time_test.go index ae53924..ff44378 100644 --- a/internal/json/time_test.go +++ b/internal/json/time_test.go @@ -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") + } + }) + } +} diff --git a/internal/testcases.go b/internal/testcases.go index 654ff1d..63ca307 100644 --- a/internal/testcases.go +++ b/internal/testcases.go @@ -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 +}