mirror of
https://github.com/rs/zerolog
synced 2026-06-08 17:13:30 +00:00
f6fbd330be
* Test coverage improvements Implements #397 and #591, might help with #473 Test coverage for core is 97.6% with only real fringe cases remaining. Improve `Fields` `isNilValue()` portability. Added a new global handler `FatalExitFunc` to allow intercepting `Fatal()` messages. Fixed CBOR float constants (removed the CBOR prefix) Added tests for: - global `FatalExitFunc` to allow intercepting Fatal messages (both for testing and public use). - `Logger` - `DisableSampling` - `.With()` copying existing context if present. - `.With().Fields()` all forms of `ErrorStackMarshaler` returns. - `.WithLevel()` for `FatalLevel`, `PanicLevel`, and `DisabledLevel`. - `.Err()` with `nil` and non-`nil` `error` and test the resulting log level. - `.should()` covering `nil` writer. - `.Output()` gets context values. - `.UpdateContext()` on a disabled logger doesn't panic and is a nop. - `.With()` all forms of `ErrorStackMarshaler` returns. - with a `nil`writer. - `.Hook()` passing no hooks. - `Array` - `.MarshalZerologArray` is a nop that won't panic. - `Context` - ` .Err()` and `.AnErr()` for `nil` errors and all forms of `ErrorStackMarshaler` returns. - `Event` - `.Caller()` to ensure we don't panic or add invalid information if `runtime.Caller()` fails -` .Err()` and `.AnErr()` for `nil` errors and all forms of `ErrorStackMarshaler` returns. - `Fields` - ` .appendFields()` all forms of `ErrorStackMarshaler` returns. - `HookLevel` - `.Run()` methods. - `LevelSampler` - `.Sample() methods. - `Syslog` - `.Write()`, `.WriteLevel()`, and `.Close()` methods. - `.WriteLevel()` with an `InvalidLevel`. - `Writer` - `.Write()` short write and error cases. - `MultiLevelWriter` `.WriteLevel()` and for `.Write()` error and `.Close()` cases. - test of unmarshalling a level byte returns correct error. - CBOR decodeStream - `.decodeFloat()`, `.binaryFmt()`, `.DecodeIfBinaryToString()`, `.DecodeObjectToStr()`, `.DecodeIfBinaryToBytes()`, `.decodeTagData()`, and `.decodeSimpleFloat()` - handling of invalid UTF-8 sequences - handling of UTC times. - handling of timestamps - handling of various map lengths Restructure `Event` `.caller()` so we test for ok and eliminate untestable coverage hole. Restructure `Context` `.Err()` when the `ErrorStackMarshaler` returns a `nil` so there's code to cover. Inverted logic for`Event` `.Caller()`'s call to `runtime.Caller()` for simpler testing. Inverted logic for `Array` `.putArray()` and `Event` `.putEvent()` so there isn't uncoverable code. Restructure `Field` `.appendFieldList()` to early return when `ErrorStackMarshaler` returns a `nil` Added comments for things we can't get coverage on. Did a go fmt ./... Coverage of core is now 100% on `Array`, `Context`, `Ctx`, `Event`, `Field`, `Hook`, and `Syslog`. Coverage of `Globals`, `Log`, `Sampler`, and `Writer` is almost all except some real edge-cases. JSON encoder coverage is 100% CBOR encoder coverage is 96.3% with base, cbor, string, time and types at 100% and decode_stream (which is lacks coverage on some panic states, and two incorrect coverage-tool lapses) * Fix CBOR tests for StackMarshaler Forgot to use the `decodeIfBinaryToString()`
183 lines
3.7 KiB
Go
183 lines
3.7 KiB
Go
//go:build !binary_log
|
|
// +build !binary_log
|
|
|
|
package zerolog
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
var samplers = []struct {
|
|
name string
|
|
sampler func() Sampler
|
|
total int
|
|
wantMin int
|
|
wantMax int
|
|
}{
|
|
{
|
|
"BasicSampler_1",
|
|
func() Sampler {
|
|
return &BasicSampler{N: 1}
|
|
},
|
|
100, 100, 100,
|
|
},
|
|
{
|
|
"BasicSampler_5",
|
|
func() Sampler {
|
|
return &BasicSampler{N: 5}
|
|
},
|
|
100, 20, 20,
|
|
},
|
|
{
|
|
"BasicSampler_0",
|
|
func() Sampler {
|
|
return &BasicSampler{N: 0}
|
|
},
|
|
100, 0, 0,
|
|
},
|
|
{
|
|
"RandomSampler",
|
|
func() Sampler {
|
|
return RandomSampler(5)
|
|
},
|
|
100, 10, 30,
|
|
},
|
|
{
|
|
"RandomSampler_0",
|
|
func() Sampler {
|
|
return RandomSampler(0)
|
|
},
|
|
100, 0, 0,
|
|
},
|
|
{
|
|
"BurstSampler",
|
|
func() Sampler {
|
|
return &BurstSampler{Burst: 20, Period: time.Second}
|
|
},
|
|
100, 20, 20,
|
|
},
|
|
{
|
|
"BurstSampler_0",
|
|
func() Sampler {
|
|
return &BurstSampler{Burst: 0, Period: time.Second}
|
|
},
|
|
100, 0, 0,
|
|
},
|
|
{
|
|
"BurstSamplerNext",
|
|
func() Sampler {
|
|
return &BurstSampler{Burst: 20, Period: time.Second, NextSampler: &BasicSampler{N: 5}}
|
|
},
|
|
120, 40, 40,
|
|
},
|
|
}
|
|
|
|
func TestSamplers(t *testing.T) {
|
|
for i := range samplers {
|
|
s := samplers[i]
|
|
t.Run(s.name, func(t *testing.T) {
|
|
sampler := s.sampler()
|
|
got := 0
|
|
for t := s.total; t > 0; t-- {
|
|
if sampler.Sample(0) {
|
|
got++
|
|
}
|
|
}
|
|
if got < s.wantMin || got > s.wantMax {
|
|
t.Errorf("%s.Sample(0) == true %d on %d, want [%d, %d]", s.name, got, s.total, s.wantMin, s.wantMax)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func BenchmarkSamplers(b *testing.B) {
|
|
for i := range samplers {
|
|
s := samplers[i]
|
|
b.Run(s.name, func(b *testing.B) {
|
|
sampler := s.sampler()
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
for pb.Next() {
|
|
sampler.Sample(0)
|
|
}
|
|
})
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestBurst(t *testing.T) {
|
|
sampler := &BurstSampler{Burst: 1, Period: time.Second}
|
|
|
|
t0 := time.Now()
|
|
now := t0
|
|
mockedTime := func() time.Time {
|
|
return now
|
|
}
|
|
|
|
TimestampFunc = mockedTime
|
|
defer func() { TimestampFunc = time.Now }()
|
|
|
|
scenario := []struct {
|
|
tm time.Time
|
|
want bool
|
|
}{
|
|
{t0, true},
|
|
{t0.Add(time.Second - time.Nanosecond), false},
|
|
{t0.Add(time.Second), true},
|
|
{t0.Add(time.Second + time.Nanosecond), false},
|
|
}
|
|
|
|
for i, step := range scenario {
|
|
now = step.tm
|
|
got := sampler.Sample(NoLevel)
|
|
if got != step.want {
|
|
t.Errorf("step %d (t=%s): expect %t got %t", i, step.tm, step.want, got)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestLevelSampler(t *testing.T) {
|
|
// Create mock samplers that return true for specific levels
|
|
traceSampler := &BasicSampler{N: 1} // Always sample
|
|
debugSampler := &BasicSampler{N: 0} // Never sample
|
|
infoSampler := &BasicSampler{N: 1} // Always sample
|
|
warnSampler := &BasicSampler{N: 0} // Never sample
|
|
errorSampler := &BasicSampler{N: 1} // Always sample
|
|
|
|
sampler := LevelSampler{
|
|
TraceSampler: traceSampler,
|
|
DebugSampler: debugSampler,
|
|
InfoSampler: infoSampler,
|
|
WarnSampler: warnSampler,
|
|
ErrorSampler: errorSampler,
|
|
}
|
|
|
|
// Test each level
|
|
if !sampler.Sample(TraceLevel) {
|
|
t.Error("TraceLevel should be sampled")
|
|
}
|
|
if sampler.Sample(DebugLevel) {
|
|
t.Error("DebugLevel should not be sampled")
|
|
}
|
|
if !sampler.Sample(InfoLevel) {
|
|
t.Error("InfoLevel should be sampled")
|
|
}
|
|
if sampler.Sample(WarnLevel) {
|
|
t.Error("WarnLevel should not be sampled")
|
|
}
|
|
if !sampler.Sample(ErrorLevel) {
|
|
t.Error("ErrorLevel should be sampled")
|
|
}
|
|
|
|
// Test levels not covered by the LevelSampler sampler (FatalLevel, PanicLevel, NoLevel) - should return true
|
|
if !sampler.Sample(FatalLevel) {
|
|
t.Error("FatalLevel should return true when no sampler is set")
|
|
}
|
|
if !sampler.Sample(PanicLevel) {
|
|
t.Error("PanicLevel should return true when no sampler is set")
|
|
}
|
|
if !sampler.Sample(NoLevel) {
|
|
t.Error("NoLevel should return true when no sampler is set")
|
|
}
|
|
}
|