New TestWriter for logging to testing.TB (#369)

This commit is contained in:
Edward McFarlane 2021-10-05 00:07:40 +01:00 committed by GitHub
parent 197adb44cc
commit 78448ee023
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 135 additions and 15 deletions

View File

@ -1,7 +1,12 @@
package zerolog package zerolog
import ( import (
"bytes"
"io" "io"
"path"
"runtime"
"strconv"
"strings"
"sync" "sync"
) )
@ -96,3 +101,54 @@ func MultiLevelWriter(writers ...io.Writer) LevelWriter {
} }
return multiLevelWriter{lwriters} return multiLevelWriter{lwriters}
} }
// TestingLog is the logging interface of testing.TB.
type TestingLog interface {
Log(args ...interface{})
Logf(format string, args ...interface{})
Helper()
}
// TestWriter is a writer that writes to testing.TB.
type TestWriter struct {
T TestingLog
// Frame skips caller frames to capture the original file and line numbers.
Frame int
}
// NewTestWriter creates a writer that logs to the testing.TB.
func NewTestWriter(t TestingLog) TestWriter {
return TestWriter{T: t}
}
// Write to testing.TB.
func (t TestWriter) Write(p []byte) (n int, err error) {
t.T.Helper()
n = len(p)
// Strip trailing newline because t.Log always adds one.
p = bytes.TrimRight(p, "\n")
// Try to correct the log file and line number to the caller.
if t.Frame > 0 {
_, origFile, origLine, _ := runtime.Caller(1)
_, frameFile, frameLine, ok := runtime.Caller(1 + t.Frame)
if ok {
erase := strings.Repeat("\b", len(path.Base(origFile))+len(strconv.Itoa(origLine))+3)
t.T.Logf("%s%s:%d: %s", erase, path.Base(frameFile), frameLine, p)
return n, err
}
}
t.T.Log(string(p))
return n, err
}
// ConsoleTestWriter creates an option that correctly sets the file frame depth for testing.TB log.
func ConsoleTestWriter(t TestingLog) func(w *ConsoleWriter) {
return func(w *ConsoleWriter) {
w.Out = TestWriter{T: t, Frame: 6}
}
}

View File

@ -1,10 +1,12 @@
// +build !binary_log //go:build !binary_log && !windows
// +build !windows // +build !binary_log,!windows
package zerolog package zerolog
import ( import (
"bytes"
"errors" "errors"
"fmt"
"io" "io"
"reflect" "reflect"
"testing" "testing"
@ -53,45 +55,45 @@ func TestResilientMultiWriter(t *testing.T) {
writers []io.Writer writers []io.Writer
}{ }{
{ {
name: "All valid writers", name: "All valid writers",
writers: []io.Writer{ writers: []io.Writer{
mockedWriter { mockedWriter{
wantErr: false, wantErr: false,
}, },
mockedWriter { mockedWriter{
wantErr: false, wantErr: false,
}, },
}, },
}, },
{ {
name: "All invalid writers", name: "All invalid writers",
writers: []io.Writer{ writers: []io.Writer{
mockedWriter { mockedWriter{
wantErr: true, wantErr: true,
}, },
mockedWriter { mockedWriter{
wantErr: true, wantErr: true,
}, },
}, },
}, },
{ {
name: "First invalid writer", name: "First invalid writer",
writers: []io.Writer{ writers: []io.Writer{
mockedWriter { mockedWriter{
wantErr: true, wantErr: true,
}, },
mockedWriter { mockedWriter{
wantErr: false, wantErr: false,
}, },
}, },
}, },
{ {
name: "First valid writer", name: "First valid writer",
writers: []io.Writer{ writers: []io.Writer{
mockedWriter { mockedWriter{
wantErr: false, wantErr: false,
}, },
mockedWriter { mockedWriter{
wantErr: true, wantErr: true,
}, },
}, },
@ -110,4 +112,66 @@ func TestResilientMultiWriter(t *testing.T) {
} }
writeCalls = 0 writeCalls = 0
} }
} }
type testingLog struct {
testing.TB
buf bytes.Buffer
}
func (t *testingLog) Log(args ...interface{}) {
if _, err := t.buf.WriteString(fmt.Sprint(args...)); err != nil {
t.Error(err)
}
}
func (t *testingLog) Logf(format string, args ...interface{}) {
if _, err := t.buf.WriteString(fmt.Sprintf(format, args...)); err != nil {
t.Error(err)
}
}
func TestTestWriter(t *testing.T) {
tests := []struct {
name string
write []byte
want []byte
}{{
name: "newline",
write: []byte("newline\n"),
want: []byte("newline"),
}, {
name: "oneline",
write: []byte("oneline"),
want: []byte("oneline"),
}, {
name: "twoline",
write: []byte("twoline\n\n"),
want: []byte("twoline"),
}}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tb := &testingLog{TB: t} // Capture TB log buffer.
w := TestWriter{T: tb}
n, err := w.Write(tt.write)
if err != nil {
t.Error(err)
}
if n != len(tt.write) {
t.Errorf("Expected %d write length but got %d", len(tt.write), n)
}
p := tb.buf.Bytes()
if !bytes.Equal(tt.want, p) {
t.Errorf("Expected %q, got %q.", tt.want, p)
}
log := New(NewConsoleWriter(ConsoleTestWriter(t)))
log.Info().Str("name", tt.name).Msg("Success!")
tb.buf.Reset()
})
}
}