Test coverage improvements#748
Conversation
There was a problem hiding this comment.
Pull request overview
This pull request significantly improves test coverage for the zerolog library, bringing core coverage to 97.6% with several modules reaching 100%. The changes implement feature requests from issues #397 (exit function override) and #591 (Fatal call hooks), and help with #473 (Fatal behavior when disabled).
Changes:
- Added
FatalExitFuncglobal handler to allow interceptingFatal()calls, enabling cleanup operations and testing - Fixed CBOR float constants by removing the type prefix bytes (previously incorrectly included)
- Refactored
isNilValue()function to use reflection instead of unsafe pointer operations for better portability - Added comprehensive test coverage for Logger, Event, Context, Array, Hook, Sampler, Writer, Syslog, and CBOR components
- Refactored several methods with inverted logic to eliminate untestable code branches and improve readability
Reviewed changes
Copilot reviewed 19 out of 20 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| globals.go | Added FatalExitFunc global variable to override fatal exit behavior |
| log.go | Updated Fatal() to use FatalExitFunc and reformatted documentation |
| fields.go | Replaced unsafe pointer-based isNilValue() with reflection-based implementation |
| event.go | Inverted logic in putEvent() and caller() for better testability and added early return in Err() for nil stack marshaler results |
| array.go | Inverted logic in putArray() for consistency with putEvent() |
| context.go | Added early return in Err() for nil stack marshaler results |
| ctx.go | Reformatted documentation comments |
| console.go | Minor formatting alignment in struct initialization |
| internal/cbor/cbor.go | Fixed float constants by removing CBOR type prefix bytes |
| internal/cbor/string_test.go | Added tests for invalid UTF-8, embedded JSON, and embedded CBOR |
| internal/cbor/decoder_test.go | Added comprehensive decoder tests including float decoding, timestamps, embedded types, and edge cases |
| internal/testcases.go | Added IPv6 test case |
| writer_test.go | Added tests for LevelWriterAdapter, SyncWriter, MultiLevelWriter, FilteredLevelWriter, and error conditions |
| syslog_test.go | Fixed Write return value and added tests for all syslog levels, Close, and invalid level handling |
| sampler_test.go | Added LevelSampler tests covering all log levels |
| log_test.go | Added extensive tests for DisableSampling, stacked With(), WithLevel for all levels, Fatal/Panic disabled states, nil writer, Output with context, and UpdateContext on disabled logger |
| hook_test.go | Added tests for Hook with no hooks and LevelHook covering all levels |
| event_test.go | Added tests for Caller with runtime failure and ErrorStackMarshaler with various return types |
| context_test.go | Added tests for ErrorStackMarshaler with various return types in context operations |
| array_test.go | Added test for MarshalZerologArray no-op method |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
IDisposable
left a comment
There was a problem hiding this comment.
@rs this gets zerolog to almost complete code coverage and adds the global FatalExitFunc feature which has been requested (and I needed to be able to cover those parts of Logger.
I think this should wrap up pretty much everything I was expecting to address here, so a release would be welcome, and I would be happy to address any outstanding issues you would like addressed.
Implements rs#397 and rs#591, might help with rs#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)
e602c63 to
5d758ff
Compare
Forgot to use the `decodeIfBinaryToString()`
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 19 out of 20 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
A nil return from ErrorStackMarshaler means no stack trace is available, not that the error itself should be omitted. Restore pre-v1.35 behavior where Err() continues to call AnErr() so the error field is always recorded. Fixes a regression introduced in rs#748. Signed-off-by: Michael Walsh <michael.walsh@elastic.co>
A nil return from ErrorStackMarshaler means no stack trace is available, not that the error itself should be omitted. Restore pre-v1.35 behavior where Err() continues to call AnErr() so the error field is always recorded. Fixes a regression introduced in rs#748. Signed-off-by: Michael Walsh <michael.walsh@elastic.co>
Implements #397 and #591, might help with #473
Test coverage for core is 97.6% with only real fringe cases remaining.
Improve
FieldsisNilValue()portability.Added a new global handler
FatalExitFuncto allow interceptingFatal()messages.Fixed CBOR float constants (removed the CBOR prefix)
Added tests for:
FatalExitFuncto allow intercepting Fatal messages (both for testing and public use).LoggerDisableSampling.With()copying existing context if present..With().Fields()all forms ofErrorStackMarshalerreturns..WithLevel()forFatalLevel,PanicLevel, andDisabledLevel..Err()withniland non-nilerrorand test the resulting log level..should()coveringnilwriter..Output()gets context values..UpdateContext()on a disabled logger doesn't panic and is a nop..With()all forms ofErrorStackMarshalerreturns.nilwriter..Hook()passing no hooks.Array.MarshalZerologArrayis a nop that won't panic.Context.Err()and.AnErr()fornilerrors and all forms ofErrorStackMarshalerreturns.Event.Caller()to ensure we don't panic or add invalid information ifruntime.Caller()fails.Err()and.AnErr()fornilerrors and all forms ofErrorStackMarshalerreturns.Fields.appendFields()all forms ofErrorStackMarshalerreturns.HookLevel.Run()methods.LevelSamplerSyslog.Write(),.WriteLevel(), and.Close()methods..WriteLevel()with anInvalidLevel.Writer.Write()short write and error cases.MultiLevelWriter.WriteLevel()and for.Write()error and.Close()cases..decodeFloat(),.binaryFmt(),.DecodeIfBinaryToString(),.DecodeObjectToStr(),.DecodeIfBinaryToBytes(),.decodeTagData(), and.decodeSimpleFloat()Restructure
Event.caller()so we test for ok and eliminate untestable coverage hole.Restructure
Context.Err()when theErrorStackMarshalerreturns anilso there's code to cover.Inverted logic for
Event.Caller()'s call toruntime.Caller()for simpler testing.Inverted logic for
Array.putArray()andEvent.putEvent()so there isn't uncoverable code.Restructure
Field.appendFieldList()to early return whenErrorStackMarshalerreturns anilAdded 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, andSyslog.Coverage of
Globals,Log,Sampler, andWriteris 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 it lacks coverage on some panic states, and two incorrect coverage-tool lapses)